TCPサーバの接続情報と、以下のファイルが与えられた。
Information to connect to a TCP server, and these files were given:
chall
ld-2.31.so
libc-2.31.so
main.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
.