所以我有这个功能:
void print_usage(char* arg)
{
char buffer[640];
sprintf(buffer, "Usage: %s [options]\n"
"Randomly generates a password, optionally writes it to /etc/shadow\n"
"\n"
"Options:\n"
"-s, --salt <salt> Specify custom salt, default is random\n"
"-e, --seed [file] Specify custom seed from file, default is from stdin\n"
"-t, --type <type> Specify different encryption method\n"
"-v, --version Show version\n"
"-h, --help Show this usage message\n"
"\n"
"Encryption types:\n"
" 0 - DES (default)\n"
" 1 - MD5\n"
" 2 - Blowfish\n"
" 3 - SHA-256\n"
" 4 - SHA-512\n", arg);
printf(buffer);
}
我希望利用格式字符串漏洞攻击(我的任务)。这是我的尝试:
我有一个漏洞利用程序,用noops和shell代码填充缓冲区(我用这个程序来缓冲溢出相同的函数,所以我知道它很好)。现在,我做了一个文件的对象转储来找到.dtors_list地址,我得到0x0804a20c,加上4个字节来得到结束我得到0x804a210。
接下来,我使用gdb查找运行程序时我的noops开始的地址。使用这个我得到了0xffbfdbb8。
所以到目前为止,我觉得我是正确的,现在我知道我想使用格式字符串将noop地址复制到我的.dtors_end地址。这是我提出的字符串(这是我作为函数的用户输入提供的字符串):
“\ X10 \ XA2 \ X04 \ X08 \ X11 \ XA2 \ X04 \ X08 \ X12 \ XA2 \ X04 \ X08 \ X13 \ XA2 \ X04 \ X08 %%。168U %% 1 $ N %%。51U% %2 $ N %%。228u %% 3 $ N %%。64U %% 4 $ N“
这对我不起作用。该程序正常运行,%s被替换为我输入的字符串(减去前面的小端内存地址,由于某种原因,两个百分号现在是百分号)。
无论如何,我有点难过,任何帮助都会受到赞赏。
答案 0 :(得分:0)
免责声明:我不是专家。
您将"\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%%.168u%%1$n%%.51u%%2$n%%.228u%%3$n%%.64u%%4$n"
作为arg
的值传递?这意味着buffer
将包含
"Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%.168u%1$n%.51u%2$n%.228u%3$n%.64u%4$n [options]\x0aRandomly..."
现在让我们进一步假设您处于x86-32目标上(如果您使用的是x86-64,这将无效),并且您正在使用不会放置任何内容的优化级别进行编译在print_usage
的堆栈帧中,除了640字节的buffer
数组。
然后printf(buffer)
将按顺序执行以下操作:
&buffer
。printf
... "Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08"
(23个字节的序列)。%.168u
:将printf
的下一个参数解释为unsigned int,并将其打印在宽度为168的字段中。由于printf
没有下一个参数,因此实际上将打印堆栈中的下一件事;也就是说,buffer
的前四个字节;即"Usag"
(0x67617355
)。%1$n
:将printf
的第二个参数解释为指向int的指针,并在该位置存储23 + 168。这会将0x000000bf
存储在位置0x67617355
中。这是您的主要问题:您应该使用%2$n
代替%1$n
并在arg
的前面添加一个垃圾字节。 (顺便提一下,请注意GNU说"If any of the formats has a specification for the parameter position all of them in the format string shall have one. Otherwise the behavior is undefined."所以你应该通过1$
添加%u
到你所有的%.51u
只是为了安全起见。)%2$n
:打印另外51个字节的垃圾。printf
:将0x000000f2
的第三个参数解释为指向int并在该垃圾位置存储%3$n
的指针。如上所述,这应该是"Usage: "
。因此,您的主要错误是您忘记了0xffbfdbb8
前缀。
我假设您尝试将四个字节0x804a210
存储到地址0x804a210
中。假设你已经开始工作了。但那么下一步将是什么?如何让程序将sprintf
处的四字节数量视为函数指针并跳过它?
利用此代码的传统方法是利用"%n"
中的缓冲区溢出,而不是printf
中更复杂的arg
漏洞。您只需要使print_usage
长约640个字符,并确保其对应于0xffbfdbb8
的返回地址的4个字节包含您的NOP底座的地址。
即使那部分也很棘手。您可能会遇到与ASLR相关的事情:仅仅因为您的雪橇在一次运行中存在于地址{{1}}并不意味着它将在下一次运行中存在于同一地址。
这有帮助吗?