这道题又是新的题型,研究了以下,要好好记录下来。
首先是看程序开启了哪些保护:
发现没开启栈溢出保护,我们继续往下看程序的逻辑:
一个简洁的页面,到此为止我们并不能看出可以利用哪些攻击方式,我们跟踪一下函数encrypt
和begin
发现begin只是一个简单的显示程序,而重点在于encrypt
函数,我们观察其程序内容:
int encrypt()
{size_t v0; // rbxchar s[48]; // [rsp+0h] [rbp-50h] BYREF__int16 v3; // [rsp+30h] [rbp-20h]memset(s, 0, sizeof(s));v3 = 0;puts("Input your Plaintext to be encrypted");gets(s);while ( 1 ){v0 = (unsigned int)x;if ( v0 >= strlen(s) )break;if ( s[x] <= '`' || s[x] > 122 ){if ( s[x] <= 64 || s[x] > 90 ){if ( s[x] > 47 && s[x] <= 57 )s[x] ^= 0xFu;}else{s[x] ^= 0xEu;}}else{s[x] ^= 0xDu;}++x;}puts("Ciphertext");return puts(s);
}
我们查看字符串,发现并没有看到想要的system
和\bin\sh\
,也就是说我们要自己构造一个ROP链来实现攻击。但是这里有一个问题,这个程序会对输入的s字符串进行加密,也就是说会破坏我们的ROP链,同时我们也可以看到加密的位数是根据字符串的长度进行判断的strlen(s)
,这里有个小技巧:strlen(s)
是通过'\0'的位置来进行对字符长度的判断的。也就是说,我们只要将字符串的首位设置为\0
,那么程序逻辑就会判断要加密的程序长度为0,这样就不会对我们构造的ROP链进行破坏,接下来我们就可以利用栈溢出来构造我们的攻击方式了。
那么我们需要思考一下这类题的做法是什么呢?他什么后门函数也没给,即使栈溢出也不知道咋整啊?我们整理步骤如下:
- 利用一个程序已经执行过的函数去泄露它在程序中的地址,然后取这个地址的末三位,然后取查找他使用的libc版本
- 程序里的函数地址和它所使用的libc库中的函数地址是不一样的,他们之间存在一个偏移地址。我们通过
offset = 函数中的地址 - libc中的函数地址
取到这个偏移量 - 得到偏移量之后之后我们可以通过
程序中函数地址 = 函数地址 + offset
的方式来找到其他函数在程序中的地址,然后我们就可以去构造rop链了
现在我们首先需要通过已经被执行的puts
函数,利用他的plt和got地址来泄露我们的libc版本,我们构造以下程序:
我们得到了puts在程序中的地址的末九位9c0
接下来我们需要拿到它的libc版本,有两种方法:
- 使用它libc检查的在线网站
- 使用LibcSearcher库实现
我们完成下面的libc定位使用,然后构造我们自己的ROP
这里我们需要注意几个地方: - 一是我们获取我们的地址时我们并不知道会收到几条内容,一开始可以全部打印出来,然后收集你需要的信息
- 二是关于libc的库要多进行几次尝试,如果都不正确就需要考虑是不是哪里出了问题
- 三是关于构造ROP链时,我们需要在其前面添加一个
ret
实现栈对齐,关于这个我暂时还没有搞清楚,之后可能会继续加以记载吧
执行攻击脚本,我们最终可以得到我们想要的构造程序,执行得到: