TCPサーバの接続情報と、以下のファイルが与えられた。
Information to connect to a TCP server, and these files were given:
challld-2.31.solibc-2.31.somain.c
main.c は、以下の処理をするプログラムだった。
size に値を読み込む。size が負または5より大きい場合、exit(0); で終了する。size に基づいて決まる大きさの領域を確保し、ポインタをローカル変数 arr に格納する。index に値を読み込む。size 以上の場合、exit(0); で終了する。arr[index] に値を読み込む。領域の確保は、以下のマクロを用いて行われていた。
main.c was a program doing these:
size.size read is negative or larger than 5, stop execution via exit(0);.size read and store its pointer to a local variable arr.index.size or more, stop execution via exit(0);.arr[index].The allocation was done via these macros:
これは (long*)alloca((n + 1 * sizeof(long))) に展開され、n の値に sizeof(long) が掛からないので、確保する領域が小さくなりそうである。
chall を objdump -d を用いて逆アセンブルした後、
GDBで最後の scanf を呼ぶ直前にブレークポイントを置いてスタックの内容を調べると、以下のようになった。
This will be expanded to (long*)alloca((n + 1 * sizeof(long))).
sizeof(long) won't be multiplied to the value of n, so the size to be allocated will be smaller than expected.
I disassembled chall via objdump -d on
After that, I checked the contents in the stack using GDB by putting a breakpoint just before calling the last scanf. This is the result:
ここから、以下のことが読み取れる。
arrが指す配列の先頭アドレスは 0x7fffffffe230 であり、スタックトップもこのアドレスである。size の値になっている。arr の値になっている。$rbp が指している要素の次) が、関数 main のリターンアドレスになっている。
したがって、配列の4要素目に大きい値を書き込むことで、値を書き込めるオフセットの範囲を増やすことができる。
さらに、配列の6要素目を書き換えることで、値を書き込むアドレスを自由に変えることができる。
これを用いて .plt.sec が参照する exit のアドレスを書き換えることで、exit(0); で実行する処理を変えることができる。
また、chall には以下の部分がある。
From this result, I found these facts:
arr is 0x7fffffffe230. The stack top is also this address.size.arr.$rbp) is the return address of the function main.
This implies that writing some large value to the 4th element of the array will extend the maximum offset to write a value,
and that we can arbitrary set the address to write by setting the 6th element of the array.
We can change what is executed via exit(0); by using this feature to set tha address of exit used in .plt.sec.
Also note that the file chall has this part:
この部分は、以下のgadgetとして用いることができる。
0x4013dc : 4要素をpopしてret0x4013e1 : %rsiに値をpopし、さらに1要素をpopしてret0x4013e3 : %rdiに値をpopしてret0x4013e4 : ret
exit(0); として実行するアドレスを「4要素をpopしてret」のものにすると、exit(0); を呼び出した時のリターンアドレスに加えてさらに3要素をpopし、3要素目からROPを実行できる。
さらに、以下のように値を配置し、printf 関数によって main 関数のリターンアドレスを出力した後、もう一度 main 関数を実行するようにした。
0x4013dc (4要素をpopしてret)0x4013e1 (%rsiに値 (9要素目のリターンアドレス) をpopし、さらに1要素をpopしてret)0x4013e3 (%rdiに値をpopしてret)0x40200b (文字列 "%ld" のアドレス)0x4013e4 (アラインメント調整用のret)0x401090 (printf)0x4013e4 (アラインメント調整用のret)0x4011b6 (main)
そして、6要素目 (書き込み先のアドレス) に exit として呼び出すアドレスのアドレス 0x404038 を書き込み、
exit として呼び出すアドレスとして 0x4013dc (4要素をpopしてret) を設定した。
最後に -1 を入力して exit(0); を呼び出させることで、用意した処理を実行させた。
ここまでを行う入力が以下である。
This part is useful as these gadgets:
0x4013dc : POP 4 elements and RET0x4013e1 : POP a value to %rsi, POP another element, and RET0x4013e3 : POP a value to %rdi and RET0x4013e4 : RET
Setting the address to execute as exit(0); to one of "POP 4 elements and RET", it will POP the return address recorded by the call of exit(0); and 3 more elements, and will execute ROP from the 3rd element.
Also, I placed values as described below to have it print the return address of the function main via the function printf, and execute the function main again.
0x4013dc (POP 4 elements and RET)0x4013e1 (POP a value (the return address, which is placed as the 9th element) to %rsi, POP another element and RET)0x4013e3 (POP a value to %rdi and RET)0x40200b (The address of the string "%ld")0x4013e4 (RET for adjusting the stack alignment)0x401090 (printf)0x4013e4 (RET for adjusting the stack alignment)0x4011b6 (main)
After that, I put 0x404038, which is the address of the address to be called as exit to the 6th element (where to write),
and set the address to be called as exit to 0x4013dc (POP 4 elements and RET).
Finally, I executed these steps by entering -1 to have it call exit(0);.
This is my input for these process:
この入力を送信すると、例えば 140525264064643 が出力された。
これは16進数にすると 0x7fce96725083 である。
この下3桁を取り、CS50 Sandboxで objdump -d libc-2.31.so をした結果から 083: を検索すると、main関数を呼び出していると推測できる以下の部分が見つかった。
Sending this input, 140525264064643 was printed, for example.
This value is 0x7fce96725083 in hexadecimal.
Taking the least 3 digits, I searched for 083: from the result of objdump -d libc-2.31.so on CS50 Sandbox.
As a result, I found this part, which looks calling the function main:
さらに、同じダンプ内を検索することで、ダンプにおけるsystem関数のアドレスは 0x52290 だとわかった。
これらの情報を用いて、system("/bin/sh"); を実行させた。
まず、1回目と同様に4要素目に大きい値を書き込み、値を書き込む範囲の制限を解除した。
そして、以下の値を書き込んだ。
0x4013dc (4要素をpopしてret)0x4013e3 (%rdiに値をpopしてret)0x404040 (exit として呼び出すアドレスの次の要素のアドレス)main関数のリターンアドレス + 0x52290 - 0x24083 (system)
さらに、6要素目に 0x404038 を書き込むことで書き込み先を切り替えた後、以下の値を書き込んだ。
0x4013dc (4要素をpopしてret)0x68732f6e69622f ("/bin/sh")
最後に exit(0); を呼び出させるために -1 を送信すると、シェルを起動できた。
すなわち、以下を送信することで、シェルを起動できた。
(address_of_system は、1回目のROPで得られた main 関数のリターンアドレスに 188941 を足した値を10進数で表した文字列に置き換える)
I also found that the address in the dump of the function system is 0x52290 by searching from the dump.
I had it execute system("/bin/sh"); using these information.
Firstly, as I did in the first part, I put some large value to the 4th element to disable the limitation of the range of where to write values.
Then, I put these values:
0x4013dc (POP 4 elements and RET)0x4013e3 (POP a value to %rdi and RET)0x404040 (The address of the next element of the address to be called as exit)main + 0x52290 - 0x24083 (system)
After that, I put 0x404038 to the 6th element to switch where to put values, and then put these values:
0x4013dc (POP 4 elements and RET)0x68732f6e69622f ("/bin/sh")
Finally, I sent -1 to have it call exit(0); and launch the shell.
In other words, I succeeded to launch the shell by sending this:
(address_of_system should be replaced with the return address of the function main, obtained in the first ROP, plus 188941, represented in decimal)
起動したシェルで ls -al コマンドを実行すると、ファイル flag-c665afc224a93b0c2e4cf82abfedf180.txt があることがわかった。
続いて cat *.txt コマンドを実行すると、flagが得られた。
I executed a command ls -al in the launched shell, and found a file flag-c665afc224a93b0c2e4cf82abfedf180.txt.
I obtained the flag by executing a command cat *.txt.