原问题
User bta https://stackoverflow.com/users/79566/bta对演员阵容给出了正确的解释 - 并评论了演员阵容的不恰当之处system
.
我要补充一点:
The extern
线条充其量是奇怪的。在严格的C99下是错误的,因为没有类型,这使得它无效;在 C89 下,类型将被假定为int
。该行显示“有一个外部定义的整数称为 system,另一个称为 put”,这是不正确的 - 有一对具有这些名称的函数。该代码实际上可能“有效”,因为链接器可能会将函数与假定的整数相关联。但对于 64 位机器来说并不安全,因为指针的大小与int
。当然,代码应该包含正确的标头(<stdio.h>
for puts()
and <stdlib.h>
for system()
and exit()
, and <string.h>
for strcpy()
).
The exit(1);
在两个不同的方面都是不好的。
/usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -c nasty.c
nasty.c: In function ‘main’:
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘system’
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘puts’
nasty.c:3: warning: built-in function ‘puts’ declared as non-function
nasty.c:8: warning: implicit declaration of function ‘strcpy’
nasty.c:8: warning: incompatible implicit declaration of built-in function ‘strcpy’
nasty.c:10: warning: implicit declaration of function ‘exit’
nasty.c:10: warning: incompatible implicit declaration of built-in function ‘exit’
nasty.c: At top level:
nasty.c:1: warning: unused parameter ‘argv’
不好的代码!我担心包含此类代码的信息源并不能解释所有的可怕之处(因为显示如此混乱的代码的唯一借口是剖析它并纠正它)。
代码中还有一个奇怪的地方:
int main(int argv,char **argc)
这是“正确的”(它会起作用),但 100% 是传统的。正常的声明是:
int main(int argc, char **argv)
这些名称是“参数计数”和“参数向量”的缩写,并使用argc
因为字符串向量(数组)的名称是不正常且完全令人困惑的。
访问所引用的站点后,您可以看到它正在经历一组分级示例。我不确定作者是否只是在 argc/argv 问题上有盲点或者故意搞乱('abo1' http://community.core-sdi.com/~gera/InsecureProgramming/abo1.html表明他正在玩,但在我看来这没有帮助)。这些示例应该可以满足您的需求,但对它们的作用没有太多解释。我不认为我可以推荐该网站。
延伸问题
这段代码中的强制转换在做什么?
#include <stdio.h>
char shellcode[] = "some shellcode";
int main(void)
{
fprintf(stdout,"Length: %d\n",strlen(shellcode));
(*(void(*)()) shellcode)();
}
它获取字符串“shellcode”的地址,并将其视为指向一个函数的指针,该函数接受一组不确定的参数,不返回任何值,并且不带参数执行它。该字符串包含某些漏洞利用的二进制汇编代码(通常运行 shell),入侵者的目标是获得 root 权限的程序来执行其 shellcode,并为他们提供具有 root 权限的命令提示符。从那时起,该系统就属于他们自己了。当然,为了练习,第一步是让一个非 root 程序来执行 shellcode。
回顾分析
分析于Mishou http://mishou.org/2010/03/14/insecure-programming-by-example-abo3-c/的网站并不像我希望的那样权威:
一、这段代码使用了C语言中的extern关键字来使system和put函数可用。它的作用(我认为)基本上是直接引用(隐含的)头文件中定义的函数的位置...我的印象是 GDB 会自动神奇地包含用于系统的头文件 stdlib.h 和用于 put 的头文件 stdio.h 。目前尚不清楚的一件事是系统地址和放置地址都写入同一位置,我认为这可能就是 gera 所说的“因此链接器不会删除它”。
剖析评论:
- 第一句话不太准确;它告诉编译器这些符号
system
and puts
在其他地方定义(作为整数)。当代码链接时,地址为puts()
- 函数已知;代码会将其视为整型变量,但整型变量的地址实际上是函数的地址 - 因此强制转换强制编译器将其视为函数指针。
- 第二句话不完全准确;链接器通过函数符号解析外部“变量”的地址
system()
and puts()
在 C 库中。
- GDB 不执行任何编译或链接过程。
- 最后一句话根本没有任何意义。地址只会写入同一位置,因为您对同一变量进行了初始化和赋值。
必须说,这并没有促使我阅读整篇文章。尽职调查迫使我前进;后来的解释更好了,尽管仍然没有我想象的那么清楚。但是,使用过长但精心设计的参数字符串来溢出缓冲区的操作是该操作的核心。代码中提到了两者puts()
and system()
这样当在非利用模式下运行时,puts()
函数是一个已知的符号(否则,你必须使用dlopen()
找到它的地址),这样当在利用模式下运行时,代码具有符号system()
可以直接使用。未使用的外部引用在可执行文件中不可用 - 当您意识到典型的系统标头中有多少个符号与包含标头的程序使用的符号数量相比,这是一件好事。
显示了一些巧妙的技巧 - 尽管这些技巧的实现没有显示在特定页面上;我假设(未经验证)信息为getenvaddr
程序可用。
abo3.c代码可以写成:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
void (*fn)(char*) = (void(*)(char*))system;
char buf[256];
fn = (void(*)(char*))puts;
strcpy(buf, argv[1]);
fn(argv[2]);
exit(1);
}
现在,它只用我最初使用的繁琐编译选项进行编译时发出一个警告 - 这就是未使用“argc”的准确警告。它与原始版本一样可被利用;但它是“更好”的代码,因为它编译干净。间接寻址是不必要的神秘,不是使代码可利用的关键部分。