TCPサーバの接続情報と、ファイル easy_kernel.tar.gz
が与えられた。
easy_kernel.tar.gz
を展開すると、以下のファイルなどが得られた。
bzImage
: initramfs.cpio.gz
: VMに配置するファイルが格納されているファイルlaunch.sh
: QEMUの起動オプションが書かれているテキストファイルvuln.ko
: ELFファイル
Information to connect to a TCP server and a file easy_kernel.tar.gz
were given.
These files and some other files are extracted from easy_kernel.tar.gz
:
bzImage
: A file used from initramfs.cpio.gz
: A file that contains files to place in the VMlaunch.sh
: A text file that has the options used on launching QEMUvuln.ko
: A ELF file
vuln.ko
を
init_func
"pwn_device"
として、proc_create
関数を呼び出す。sread
swrite
sioctl
swrite
でコピーする長さの上限を第3引数の値に設定する。
sioctl
関数で設定する値に制限は無いため、swrite
でコピーする長さの制限も実質無いといえそうである。
したがって、sread
関数のリターンアドレス以降のデータの取得や、swrite
関数のリターンアドレス以降のデータの設定ができそうである。
また、sread
関数およびswrite
関数においては、スタック上のコピーを開始する場所から 0x80 バイト先にcanaryがあり、0x90 バイト先にリターンアドレスがあることが読み取れた。
Decompiling vuln.ko
via
init_func
proc_create
with the first argument "pwn_device"
.sread
swrite
sioctl
swrite
to the value of 3rd argument if the 2nd argument is 0x20.
There are no limits on the value to set in the function sioctl
, so there virtually looks no limit on the length to copy in the function swrite
.
Therefore, reading data from the return address of the function sread
and writing data from the return address of the function swrite
looks possible.
Also, I found that there are the canary at the 0x80 bytes ahead from the point on the stack where copying starts and the return address at the 0x90 bytes ahead in the functions sread
and swrite
.
脆弱性を持っていそうな関数が見つかったので、次はそれらを実行する方法を探す。
Now I found functions that look vulnable, so nextly I searched for a way to execute these functions.
Connecting to the specified server using
指定のコマンドでhashcash stamp:
を除く部分を貼り付け、
Enterキーを押してLFを送信すると、シェルが起動した。
観察の結果、以下のことがわかった。
flag.txt
があるが、これを cat
コマンドで読もうとすると「Permission denied」と出て読めない。vuln.ko
がある。これは easy_kernel.tar.gz
に含まれているものと同じようである。/bin
、/sbin
、/usr/bin
、/usr/sbin
内のファイルは /bin/busybox
へのリンクである。 (/bin/busybox
自身は除く)/proc/pwn_device
がある。このファイルの名前は proc_create
関数に渡されていた文字列と同じである。
このファイル /proc/pwn_device
を読み書きすることで、sread
関数やswrite
関数を実行できるようである。
cat /proc/pwn_device
コマンドで読もうとすると「Bad address」と出て失敗したが、dd
コマンドを用いると読むことができた。
読み込むブロック数を表す count
は 1 に設定し、ブロックサイズを表す bs
で読み込むサイズを指定した。
すると、以下のように480バイトまでは読み込むことができたが、これを超えると「Bad address」が出てしまった。
また、読み込んだデータはod
コマンドでテキストとして出力できるが、パイプで直接 dd
コマンドの出力を od
コマンドに渡すと出力が混ざってしまった。
そのため、以下の例では、dd
コマンドで読み込んだデータを一旦ファイルに保存し、それを od
コマンドで出力している。
さらに、od
コマンドの -v
オプションで重複行の省略を抑止し、わかりやすくしている。
I executed hashcash stamp:
,
and pressed the Enter key to send LF. As a result, a shell started.
After some observation, I found these things:
flag.txt
in the root directory. Trying to read this file via cat
command failed with a message "Permission denied".vuln.ko
in the root directory. This file looks like the same as the file in easy_kernel.tar.gz
./bin
, /sbin
, /usr/bin
, and /usr/sbin
are links to /bin/busybox
. (except for /bin/busybox
itself)/proc/pwn_device
. The name of this file is the same as the string passed to the function proc_create
.
Reading and writing this file /proc/pwn_device
looks meaning to execute the functions sread
and swrite
.
Reading the file via cat /proc/pwn_device
failed with a mesasge "Bad address", but I succeeded to read the file using dd
command.
I set the number of blocks to read count
to 1, and set the size of blocks bs
as the size to read.
As a result, I succeeded to read 480 bytes like shown below, but trying to read more resulted in "Bad address".
Also, od
command is useful to print the data read as text, but passing the output of dd
command to od
command directly via pipe resulted in a mixed output.
To avoid this, I saved the output of dd
command to a file and printed the file via od
command in this example.
Moreover, I used -v
option for od
command to prevent it from omitting duplicate lines and to make it clear.
/proc/pwn_device
を dd
コマンドで読む
Reading the file /proc/pwn_device
via the dd
command
得られたデータの最初の部分は、Ghidraでsread
関数内で設定していることが読み取れた値と一致している。
さらに、解析結果通り、0x80 バイト目からはcanaryと思われる値が、0x90 バイト目からはリターンアドレスと思われる値が得られていることがわかる。
なお、このリターンアドレスは、サーバに接続し直すと変わることがわかった。
上位の ffffffff
と下位3桁の 347
は変わらず、その間の5桁が変わるようだった。
The first part in the data read matches with the values I found set in the sread
function using Ghidra.
Also, as the analysis suggested, there is a value that looks like the canary from the 0x80-th byte and one that looks like the return address from the 0x90-th byte.
I also found that the return address varies between each sessions.
The upper digits ffffffff
and the last 3 digits 347
looked fixed, and the 5 digits between them changed.
問題文に、このページへのリンクが提示されていた。
The challenge description had a link to this page:
Learning Linux Kernel Exploitation - Part 1 - Midas Blog
この記事より、以下の処理を実現できればflagを得られそうであることがわかった。
0
を与え、関数 prepare_kernel_cred
を呼び出す。commit_creds
を呼び出す。このことによりroot権限が得られる。/flag.txt
を読むための処理を行う。
さらに、呼び出す関数のアドレスは /proc/kallsyms
から読み取れること、
ユーザーモードに戻るには swapgs
命令を実行した後スタック上に戻り先のデータを配置して iretq
命令を実行すればいいことも、この記事から読み取れた。
また、記事では、これらの処理を実行させるため、リターンアドレスにアプリケーション側の関数のアドレスを設定する方法が紹介されている。
なるほど、このデータを細工してデバイスファイルの操作からアプリケーションで用意した関数を呼び出させるというのは、自分が昔xv6でやったことに似ている。
From this article, I found that I should do these things to obtain the flag:
prepare_kernel_cred
with an argument 0
.commit_creds
. This will result in gaining the root privilege./flag.txt
.
I also found that the addresses of functions to call can be read from /proc/kallsyms
,
and that we should execute swapgs
instruction and then execute iretq
instruction with data about where to return on the stack to return to user-mode.
Moreover, the article introduces a way to execute these things by setting an address of a function in the application as the return address.
Tweaking data to make operations on device files call functions in the application looks like what I did on xv6 before.
Fix vulnerabilities in exec() function by mikecat · Pull Request #8 · mit-pdos/xv6-public · GitHub
記事を参考に、以下のコマンドで呼び出す関数のアドレスを得ようとした。
Reffering to the article, I tried to obtain the addresses of functions to call by this command:
しかし、出力された各関数のアドレスは 0000000000000000
となっており、役に立たなそうだった。
「/proc/kallsyms zero」でググると、以下のページが見つかった。
However, the printed addresses of each functions were 0000000000000000
and this looked useless.
I googled "/proc/kallsyms zero" and found this page:
linux - Reading kallsyms in user-mode - Stack Overflow
この記事によれば、/proc/kallsyms
でアドレスを得るにはrootでないといけないようである。
ルートディレクトリのファイル init
を見ると、最後が以下のようになっていた。
According to this article, the user must be root to obtain addresses from /proc/kallsyms
.
I checked the file init
in the root directory, and found that the last part of the file is:
これは、rootから一般ユーザに切り替えた上でシェルを実行する、という意味だろう。
そこで、配布されたファイルを書き換え、この切り替えを無効化することでrootでシェルを実行させることにした。
initramfs.cpio.gz
を展開して得られたファイル initramfs.cpio
をバイナリエディタで開き、文字列 su -l ctf
を検索すると、0x045e22
に見つかった。
そこで、ここのsu
を #u
に書き換えることで、切り替えを無効化した。
書き換えた initramfs.cpio
を圧縮して新しい initramfs.cpio.gz
を作り、これを利用してQEMUを起動した。
すると、$
だったシェルのプロンプトが #
になっており、/proc/kallsyms
から関数のアドレスを得ることができた。
アドレスは変わってもアドレスの差は変わらないと予想し、続けて差の計算用に /proc/pwn_device
からリターンアドレスを読み出した。
This should be standing for switching from the root to a normal user and launching the shell.
Seeing this, I decided to disable this switching by modifying the given file and have it launch the shell as the root.
I extracted a file initramfs.cpio
from initramfs.cpio.gz
and opened it with a binary editor. Then, I searched for a string su -l ctf
and found that at 0x045e22
.
After that, I changed su
here to #u
to disable the switching.
I compressed the modified file initramfs.cpio
to create a new initramfs.cpio.gz
, and launched QEMU using this new file.
As a result, the prompt of the shell, which was $
, became #
and I succeeded to obtain the addresses of functions from /proc/kallsyms
.
Guessing that the differences of addresses won't change while the addresses change, I obtained the return address from /proc/pwn_device
in the same session for calculating the differences.
swrite
からアプリケーションのコードに飛ばす試み
An attempt to have it jump from swrite
to an application code
これまでの情報をもとに、以下の手順でroot権限を持ったシェルの起動を試みるプログラム attack.asm
を作成した。
なお、サーバ上にlibcが見つからず、C言語でプログラムを書いても実行できない可能性があると考えたため、アセンブリ言語で書くことにした。
/proc/pwn_device
を開く。ioctl
システムコールを用い、swrite
で書き込む長さの制限を十分大きくする。sread
から、canaryとリターンアドレスの値を読み込む。swrite
でcanaryとroot権限を得るプログラムのアドレスを書き込む。execve
システムコールを用い、/bin/sh
を実行する。
Based on these information, I created a program attack.asm
to try to launch a shell with the root privilege in these steps.
I decided to write in an assembly language because I couldn't find any libc on the server and thought that C program may be unable to be executed.
/proc/pwn_device
.swrite
enough via the system call ioctl
.sread
.swrite
./bin/sh
via the system call execve
.
これをサーバで実行するため、まず
To execute this program on the server, firstly I converted this program to an object file using
するとファイル attack.o
ができるので、これを
This command yielded a file attack.o
, so I uploaded this file to
これを以下の
Then, I compressed this and encoded to Base64 with newline characters added after each 76 characters using this Recipe on
Gzip, To Base64, Find / Replace - CyberChef
サーバに接続してHashcatの出力を貼り付けた後、シェルで以下のコマンドを実行した。
そして、変換したBase64データを貼り付け、Ctrl+D で入力を終了すした。
After connectiong to the server and pasting an output of Hashcat, I executed this command on the shell.
Then, I pasted the Base64-encoded data and pressed Ctrl+D to finish the input.
すると、ファイル attack
ができた。
chmod +x attack
コマンドでこれを実行可能にし、./attack
コマンドで実行できた。
実行した結果は、以下のエラーとなった。
This operation created a file attack
.
I executed chmod +x attack
command to make this file executable, and executed the program using ./attack
command.
Executing this program resulted in this error:
どうやら、今回の環境では、記事で紹介されていたアプリケーションのコードのアドレスを指定して飛ばす方法は使えないようである。
しかし、この試みによって、swrite
関数の実行時にエラーを起こすとレジスタの値を出力してくれることがわかった。
It looks like the way on the article that is specifying an address of an application code and having it jump there doesn't work in the environment for this challenge.
Not reaching to the goal, this try revealed that the system prints values of the registers when an error occurs while executing the function swrite
.
iretq
命令の実行
Searching for ROP gadgets and executing the iretq
instruction
アプリケーションのプログラムのアドレスを指定して実行しようとしても失敗するようなので、
「root権限を得る関数を実行してユーザーモードに戻る」処理をROP (Return-Oriented Programming) で実行することが求めらているようである。
ROPを行うためには既存のプログラムから ROP gadget と呼ばれる行いたい処理のパーツを見つけることが必要であり、これを探すために既存のプログラムのデータを取得したい。
そこで、vuln.ko
内の sread
関数のプログラムを書き換えて、リターンアドレスをコピー元として使うようにした。
具体的には、Ghidra上の表示で 0010010a
から命令 mov rsi, [rsp + 0x90]
を書き込むことにした。
この部分のプログラムを initramfs.cpio
から探すと 0x232
から見つかったので、
ここから命令の区切りを考えてNOPを加えた10バイト 48 8b b4 24 90 00 00 00 90 90
を上書きした。
書き換えた initramfs.cpio
から新しい initramfs.cpio.gz
を作り、これを用いてQEMUを起動した。
QEMUの標準出力をファイルにリダイレクトし、以下のコマンドを実行した。
Since specifying an address in an application program for execution looks failing,
It looks like the process to "call functions to gain the root privilege and return to user-mode" should be executed using ROP (Return-Oriented Programming).
Finding parts of what to do, which are called "ROP gadget", from existing program is required for ROP, so now I want to retrieve the data of the existing program.
To achieve this, I decided to modify the program of the function sread
in vuln.ko
to have it use the return address as the source of copying.
Specifically, I decided to put an instruction mov rsi, [rsp + 0x90]
from 0010010a
on Ghidra.
I searched for the program in this part from initramfs.cpio
, and found from 0x232
.
Therefore, considering the boundaries of instructions, I added some NOPs and modified 10 bytes from there to 48 8b b4 24 90 00 00 00 90 90
.
I created new initramfs.cpio.gz
from modified initramfs.cpio
and launched QEMU using this.
I redirected the standard output of QEMU to a file, and executed this command:
その結果出力されたBase64エンコードされたデータを以下のCyberChefのRecipeでデコードすることで、リターンアドレスが表す場所以降のプログラムのデータを得た。
Then, I decoded the Base64-encoded data in the result using this Recipe for CyberChef to obtain the data of the program from the return address.
From Base64, Gunzip - CyberChef
得られたプログラムのデータは、-b binary -m i386 -M x86-64 -D
オプションを使うことで、逆アセンブルできた。
得られたプログラムのデータからバイナリエディタで pop rdi; ret
を表すデータ 5f c3
を検索すると、0xac9
に見つかった。
また、iretq
を表すデータ 48 cf
を検索すると、0x5e1f
に見つかった。
しかし、%rax
(返り値) の値を %rdi
(第1引数) に移すのに役立ちそうなgadgetや、swapgs
を表すデータ 0f 01 f8
は見つけられなかった。
最初に /proc/pwn_device
からスタックの内容を dd
コマンドで読み取った結果を見直すと、
データの最後の部分はiretq
で使う各レジスタの値のようになっており、それに近い 0x130
にもプログラムのアドレスのような値があることがわかった。
そこで、先ほど initramfs.cpio
に書き込んだデータのうち 90 00 00 00
の部分を 30 01 00 00
に書き換え、同様にこのアドレス以降のプログラムのデータを得た。
得たデータを調べると、0xe2e
にswapgs
命令を実行するgadgetとして使えそうな以下の部分があった。
I disassembled the obtained program using "objdump" in -b binary -m i386 -M x86-64 -D
.
I searched for 5f c3
, which stands for pop rdi; ret
, from the program data using a binary editor and found that at 0xac9
.
I also searched for 48 cf
, which stands for iretq
, and found that at 0x5e19
.
However, I coundn't found gadgets for copying the value of %rax
(the return value) to %rdi
(the first argument), nor 0f 01 f8
, which stands for swapgs
.
Looking at the contents of stack obtained from /proc/pwn_device
via dd
command in the early step again,
I found that the last part looks like values of registers for use with iretq
, and that 0x130
, which is near the part, also has a value that looks like a program address.
Seeing this, I changed 90 00 00 00
in the data written to initramfs.cpio
in the previous step to 30 01 00 00
, and obtained the program data from this address in the same way.
Investigating the obtained data, I found this part, which looks useful as a gadget to execute the swapgs
instruction, from 0xe2e
.
そこで、これらのデータを組み合わせ、ROPによりroot権限を得るための関数を実行するプログラム attack2.asm
を作成した。
ROPで直接返り値を引数にするのは難しそうだったので、ユーザーモードに戻っても %rax
の値が維持されることを期待し、各関数をそれぞれ swrite
から呼び出させることにした。
Finding these things, I combined them and created a program attack2.asm
which is supposed to execute the functions to gain the root privilege via ROP.
Since it looked difficult to directly use the return value as an argument in ROP, I decided to call each functions from separate invocations of swrite
, hoping that the value of %rax
doesn't change on returning to user-mode.
ここで、なぜか CS50 IDE にログインすると 403 Forbidden と出てしまい、使えなくなってしまった。
そこで、かわりに t2.micro
、Ubuntu 20.04 (ami-036d46416a34a611c
)) 上でld
コマンドを実行した。
初期状態では ld
コマンドは使えず、sudo apt-get install binutils
コマンドを実行すると使えるようになった。
このプログラムの実行結果は、以下のようになった。
At this point, CS50 IDE started to show "403 Forbidden" after logging in and it stopped working for some reason.
Seeing this, I used an EC2 instance on t2.micro
、Ubuntu 20.04 (ami-036d46416a34a611c
)) instead to execute the ld
command.
The ld
command didn't work at first. It became available after executing a command sudo apt-get install binutils
.
This is the result of executing this program:
エラーの詳細は出力されず、単に「Segmentation fault」と出力されている。
解析の結果、ユーザーモードに戻った直後として設定した位置にexit
システムコールの呼び出しを配置しても Segmentation fault になったため、ユーザーモードにうまく戻れていないと考えられる。
Simply "Segmentation fault" is printed without giving the details of the error.
Some investigation revealed that this "Segmentation fault" happens even if an invocation of exit
system call is placed to be executed right after returning to user mode,
so it looks like returning to user-mode is failing.
記事に従ってiretq
命令を用い、自力でユーザーモードに戻る試みは、うまくいかなかった。
ところで、swrite
関数の呼び出し元には、swrite
関数から戻った後ユーザーモードに戻るための本来の処理があることが期待できる。
そこで、この本来の処理を用いてユーザーモードに戻ることを試みることにした。
最初に /proc/pwn_device
からスタックの内容を dd
コマンドで読み取った結果を再び見直すと、
0xe0
にもプログラムのアドレスのような値があり、これはsread
の呼び出しに繋がる関数のリターンアドレスであると推測できた。
さらに、これはあくまでsread
が呼び出された時のスタックの内容だが、swrite
においても同様の構造になると予想した。
これに基づき、ROPによりroot権限を得るための関数を実行した後、pop rdi; ret
を用いてスタックをこの値の位置まで進めることでユーザーモードに戻るプログラム attack3.asm
を作成した。
My attempts to use the iretq
instruction to return to user-mode by myself referring the article didn't succeed.
By the way, there should be the original program to return to user-mode after returning from the function swrite
in the caller of the function swrite
.
Considering this, I decided to try to return to user-mode using this original program.
Looking at the contents of stack obtained from /proc/pwn_device
via dd
command in the early step again,
I found a value that looks like a program address at 0xe0
, and I guessed that this is a return address of a function that will lead to the call of sread
.
Moreover, though this is the contents of the stack when sread
is called, I guessed that the structure will also be like this when swrite
is called.
Based on this, I created a program attack3.asm
that executes functions to gain the root privilege using ROP and then executes pop rdi; ret
to advance the stack to this value to return to user-mode.
このプログラムの実行結果は、以下のようになった。
This is the result of executing this program:
再び「Segmentation fault」が出てしまったが、swrite
が呼び出されたことを示すメッセージが1個から2個になっており、1回はユーザーモードに戻ることに成功していると考えられる。
"Segmentation fault" is printed again, but there are two messages that indicate that swrite
is called instead of one, so it looks succeeded to return to user-mode at least once.
ここで、swrite
関数の実行中にエラーが起こるとレジスタの値が出力されることを利用した調査をすることにした。
まず、以下のプログラムを実行し、swrite
関数のリターンアドレスを得た。
これは、swrite
関数のリターンアドレスをRSI
レジスタにコピーした後、ゼロ除算でエラーを起こすプログラムなので、
swrite
関数のリターンアドレスがRSI
として出てくる。
I decided to perform some investigation using the feature that prints the values of registers when an error occurs while executing the function swrite
.
Firstly, I executed this program to obtain the return address of the function swrite
.
This program copies the return address of the function swrite
to the RSI
register, and then causes an error by divding by zero.
Therefore, the return address of the function swrite
will be printed as RSI
.
アセンブルすると以下のようになる。これをGhidraにおける 00100048
に相当する、initramfs.cpio
の 0x170
から書き込んだ。
Assembling this program yields this. I put this program from 0x170
of initramfs.cpio
, which corresponds to 00100048
on Ghidra.
そして、以下のコマンドでこのプログラムを実行した。
Then, I executed this program via this command:
次に、このswrite
関数のリターンアドレスを利用して prepare_kernel_cred
関数のアドレスを求め、呼び出した後ゼロ除算を行う、以下のプログラムを用意した。
このプログラムは、prepare_kernel_cred
関数の返り値を RCX
に入れた状態でエラーを起こす。
After that, I created this program to obtain the address of the function prepare_kernel_cred
from the return address of swrite
, call the function and finally divide by zero.
This program causes an error with the return value of the function prepare_kernel_cred
in RCX
.
これをアセンブルして swrite
関数に書き込みたいが、書き換える前のプログラムで 00 00 00 00
となっている部分を書き換えるとモジュールが読み込めなくなってしまった。
そこで、これを避け、以下の配置で initramfs.cpio
に書き込んだ。xx
は書き換える前のプログラムの値を残すことを意味する。
Trying to write this program after assembling to the function swrite
, I found that modifying 00 00 00 00
in the original program makes it fail to read the module.
Therefore, I avoided this and wrote the program to initramfs.cpio
in this placement. xx
stand for preserving the original values.
このプログラムを何回か実行した結果、prepare_kernel_cred(0)
の返り値はQEMUを起動しなおすと返り値は変わるが、起動しなおさず連続で実行すると返り値は変わらなそうだった。
このことから、処理を以下の2個のプロセスに分けることを思いついた。
prepare_kernel_cred(0)
を呼び出した後エラーを起こし、返り値を出力させるプロセスcommit_creds
関数を呼び出してroot権限を得るプロセス
Executing this program several times, I found that the return value of prepare_kernel_cred(0)
changes after re-launching QEMU, but it didn't change when I executed the program in a row.
Seeing this, I came up with a way where I use these two processes:
prepare_kernel_cred(0)
to have it print the return valuecommit_creds
to gain the root privilege
まず、エラーを起こして prepare_kernel_cred(0)
の返り値を得るため、メモリアクセスができるgadgetを探した。
すると、以下のものが見つかった。
Firstly, to cause an error to obtain the return value of prepare_kernel_cred(0)
, I searched for a gadget that can access to the memory.
As a result, I found this:
なお、このgadgetは実行することで強制終了することを意図しているので、ret
は不要である。
次に、prepare_kernel_cred(0)
を呼び出した後、このgadgetを利用して0番地への書き込みを行うプログラム attack4_reveal.asm
を作成した。
Note that ret
isn't needed here because this gadget is for forcing to exit by being executed.
After that, I created a program attack4_reveal.asm
that calls prepare_kernel_cred(0)
and writes to the address 0 using this gadget.
このプログラムを実行すると、RAX
として prepare_kernel_cred(0)
の返り値が出力される。
次に、この返り値を利用するプログラムを作るにあたり、テキストで表された数値をバイナリに変換するのは大変そうなので、既存のコマンドを利用することにした。
xxd
コマンドを利用するとバイト列を表す文字列をバイト列に変換できるが、それだけだとバイトオーダーが逆になってしまう。
そこで、bswap
命令を利用してバイトオーダーを反転させ、値として利用できる形にすることにした。
これを踏まえ、prepare_kernel_cred(0)
の返り値を入力として受け取り、flagを出力するプログラム attack4_flag.asm
を作成した。
/bin/sh
を実行しようとするとエラーになってしまったので、/flag.txt
の内容を直接読み取ることにした。
The return value of prepare_kernel_cred(0)
should be printed as RAX
after executing this program.
To create a program that uses this return value, I decided to utilize existing commands because converting numbers represented as text to binary looks tough.
The xxd
command can convert strings that represents sequences of bytes to sequences of bytes, but this will yield the number with the order of bytes reversed.
Therefore, I decided to use the bswap
instruction to reverse the order of bytes and make it usable as a value.
Based on this, I created a program attack4_flag.asm
that takes the return value of prepare_kernel_cred(0)
and prints the flag.
I decided to read /flag.txt
directly because trying to launch /bin/sh
resulted in errors.
この2個のプログラムを用い、以下のようにflagが得られた。
I obtained the flag in this way, using these two programs: