TCPサーバの接続情報と、ELFファイル oversight
および libc-2.27.so
が与えられた。
oversight
を
wait
関数において、数値を読み込み、それをprintf
の何番目のデータを出力するかの指定に用いてデータを出力する。get_num_bytes
関数において、何バイト読み込むかを入力させる。(最大256バイト)echo
関数において、256バイトのローカル配列を用意する。echo_inner
関数において、ローカル配列に指定されたバイト数をfread
関数で読み込み、読み込んだデータの後ろに1バイトの0x00を書き込む。
ローカル配列は256バイトしか無いため、256バイト読み込む指定をすると、ローカル配列の1個次のバイトに0x00を書き込むことになる。
この位置にはecho
関数のプロローグで保存した%rbp
の値があり、0x00の書き込みによってこの値が小さくなることがある。
すると、get_num_bytes
関数のエピローグにおいて、この小さくなった%rbp
の値を%rsp
に書き込むため、スタックポインタが下位側にずれることがある。
その結果、スタックポインタがデータの読み込み先のローカル配列内の要素を指してくれることがあり、ROP (Return-Oriented Programming) に繋がる。
oversight
を実行し、
wait
関数でデータを出力するprintf
関数を呼び出す直前のスタックの様子を調べると、以下のようになった。
Information to connect to a TCP server and ELF files oversight
and libc-2.27.so
were given.
Decompiling oversight
via
wait
, read an integer and ouptut some data using the integer to specify which data to output in the function printf
.get_num_bytes
, read how many bytes to read. (256 bytes at maximum)echo
, allocate an 256-byte local array.echo_inner
, read the specified number of bytes to the local array, and write one-byte 0x00 after the data read.
The local array has only 256 bytes, so when it is specified to read 256 bytes, it will write 0x00 to the byte next to the local array.
The value of %rbp
is stored in the prologue of the function echo
to the area. Writing 0x00 to the are may make the value smaller.
After that, the new value of %rbp
is written to %rsp
in the epilogue of the function get_num_bytes
, and the stack pointer may be moved to an lower address.
This may make the stack pointer point at somewhere in the local array to read data and enable ROP (Return-Oriented Programming).
I executed oversight
on printf
function in the wait
function to output some data.
Here is the result:
0x7fffffffc268
にmain
関数へ戻るリターンアドレスが配置されていることが読み取れる。
さらに、main
関数では呼び出された後%rbp
をプッシュする以外にスタックにデータを確保していないため、
その2要素先の 0x7fffffffc278
にあるのがmain
関数から戻るリターンアドレスであることがわかる。
経験上printf
関数はスタック上の最初のデータを6番目として扱うはずなので、ここは27番目になる。
よって、wait
関数ではprintf
関数に27番目のデータを出力させることで、main
関数から戻るリターンアドレスがわかり、libcの位置を求めることができる。
また、readelf -s
コマンドで調べた結果、今回の libc-2.27.so
における system
関数のオフセットは 0x4f550
であった。
さらに、libc-2.27.so
からバイナリエディタで文字列 /bin/sh
を検索したところ、0x1b3e1a
バイト目 (0-origin) に見つかった。
さらに、関数呼び出し時のスタックのアライメントとecho
関数のコードを考えると、
今回の256バイトの配列は16進数の末尾が0のアドレスに配置されることがわかる。
スタックポインタが16の倍数の時に関数を呼び出した状態にするには、関数を呼び出すとスタックにリターンアドレスが積まれるので、
スタックポインタの16進数の末尾が8の状態で関数の先頭に飛ぶようにすればよい。
したがって、ROPでsystem("/bin/sh")
を呼び出す際、system
関数のアドレスは、
16進数の末尾が0のアドレス、すなわち最後から偶数番目の要素に配置するとよい。
これらを踏まえ、サーバに接続してシェルを起動する以下のプログラムを用意した。
0x00を書き込む前の保存された%rbp
の値によってはスタックが十分移動せず、シェルの起動に失敗することがあるようなので、
シェルの起動に成功するまで接続とデータの送受信をやり直すようにした。
I found that there is a return address to return to the function main
at 0x7fffffffc268
.
Also, I found that the return address to return from the function main
is at 0x7fffffffc278
, which is 2 elements ahead,
because there are no data allocated on the stack except for pushing %rbp
in the function main
.
The function printf
should treat the first element on the stack as 6th, so this element is 27th.
Therefore, we can obtain the address to return from the function main
by having the function printf
output the 27th data in the function wait
.
This is useful to know where the libc is placed on the memory.
Investigating via the readelf -s
command, I found that the offset of the function system
in libc-2.27.so
used in this challenge is 0x4f550
.
I also searched for a string /bin/sh
from the file libc-2.27.so
via a binary editor, and found on the 0x1b3e1a
-th byte (the first byte is 0th).
Considering the stack allignment on calling functions and the program for the function echo
,
the 256-byte array should be placed at an address whose last digit in hexadecimal is 0.
To execute functions as if it is called with a stack pointer which is multiple of 16,
the function should be started with the last digit of the stack pointer in hexadecimal being 8,
considering that an return address is pushed on the stack when calling a function.
Therefore, to call system("/bin/sh")
via ROP, we should place the address of the function system
to an address whose last digit in hexadecimal is 0.
In other words, it should be placed to a even-th element from the last.
Based on these findings, I created a program to connect to the server and launch the shell.
The stack may not move well and it may fail to launch the shell, depending on the value of %rbp
before writing 0x00,
so I had it repeat connection and communication until it succeeds in launching the shell.
シェルでls -al
コマンドを実行すると、ファイル flag.txt
があることがわかった。
cat flag.txt
コマンドを実行すると、flagが得られた。
Executing a command ls -al
on the shell, I found that there is a file flag.txt
.
I obtained the flag by executing a command cat flag.txt
.