先月に引き続きsyscall。思ったんですけど、AlpacaHackが毎日じゃなくなったことでwriteupを書く余裕が生まれたんですよね。いろいろ書けるといいな
Let's Shut Down Linux
rebootシステムコールに3個の引数を指定して、Linuxをシャットダウンしましょう!
※Dockerfileのビルド中に、Linuxイメージをビルドするために、100MB以上のデータのダウンロードや並列ビルドを行います。そのため、Dockerイメージのビルドに数分かかることがあります。
chal、chal.c、compose.yaml、Dockerfile、Makefile、server.pyが渡されます。実際にサーバにいるlinuxをshutdownする問題みたいですね。chal.cを見てみます。
#include <stddef.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <unistd.h>
/* Run a system call using 4 arguments. */
/* __attribute__((unused)) are used to disable warnings when build */
uint64_t __attribute__((naked))
my_syscall_4(__attribute__((unused)) uint64_t syscall_number,
__attribute__((unused)) uint64_t arg1,
__attribute__((unused)) uint64_t arg2,
__attribute__((unused)) uint64_t arg3,
__attribute__((unused)) uint64_t arg4) {
asm("mov %rdi, %rax\n"
"mov %rsi, %rdi\n"
"mov %rdx, %rsi\n"
"mov %rcx, %rdx\n"
"mov %r8, %r10\n"
"syscall\n"
"ret\n");
}
/* Run a system call using 3 arguments. */
uint64_t my_syscall_3(uint64_t syscall_number, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
return my_syscall_4(syscall_number, arg1, arg2, arg3, 0);
}
/* Run a system call using 1 argument. */
uint64_t my_syscall_1(uint64_t syscall_number, uint64_t arg1) {
return my_syscall_4(syscall_number, arg1, 0, 0, 0);
}
/* Write a string to stdout. */
ssize_t my_write(const char *s) {
size_t size = 0;
while (s[size]) {
++size;
}
return my_syscall_3(SYS_write, STDOUT_FILENO, (uint64_t)s, size);
}
/* Write a string and exit. */
void __attribute__((noreturn)) my_fatal(const char *message) {
my_write(message);
my_syscall_1(SYS_exit, 1);
__builtin_unreachable();
}
/* Read a string from stdin, then parse it as decimal number. */
uint32_t my_read_uint32() {
char buf[64] = {};
ssize_t size = (ssize_t)my_syscall_3(SYS_read, STDIN_FILENO, (uint64_t)buf, sizeof(buf) - 1);
if (size <= 0) {
my_fatal("Failed to read...\n");
}
uint32_t result = 0;
for (int i = 0; buf[i] && buf[i] != '\n'; ++i) {
char c = buf[i];
if ('0' <= c && c <= '9') {
result *= 10;
result += (c - '0');
} else {
my_fatal("Characters should be digits...\n");
}
}
return result;
}
/* An entry point function. */
int _start() {
my_write(" _ _ _ ____ _ _ ____ _ _ \n");
my_write("| | ___| |_( )___ / ___|| |__ _ _| |_ | _ \\ _____ ___ __ | | (_)_ __ _ ___ __\n");
my_write("| | / _ \\ __|// __| \\___ \\| '_ \\| | | | __| | | | |/ _ \\ \\ /\\ / / '_ \\ | | | | '_ \\| | | \\ \\/ /\n");
my_write("| |__| __/ |_ \\__ \\ ___) | | | | |_| | |_ | |_| | (_) \\ V V /| | | | | |___| | | | | |_| |> < \n");
my_write("|_____\\___|\\__| |___/ |____/|_| |_|\\__,_|\\__| |____/ \\___/ \\_/\\_/ |_| |_| |_____|_|_| |_|\\__,_/_/\\_\\\n");
my_write("Specify arg1, arg2, and arg3 in decimal to be used in the reboot system call to shut down Linux.\n");
my_write("arg1: ");
uint32_t arg1 = my_read_uint32();
my_write("arg2: ");
uint32_t arg2 = my_read_uint32();
my_write("arg3: ");
uint32_t arg3 = my_read_uint32();
my_write("Running a reboot system call!\n");
my_syscall_4(SYS_reboot, arg1, arg2, arg3, (uint64_t)"A constant string");
my_fatal("Failed to shut down. Kernel panic will occur...\n");
}
AAがいいですね。
数字を3つ渡すとrebootのsyscallに渡してくれて、shutdownに成功するとflagがもらえるみたいです。rebootに引数なんてあったかな〜と思いながらまたChromiumのドキュメントを見てみます。chalをfileコマンドしてみたところ、x86-64だと判明したので、またx86-64のテーブルを見てみます。
169 reboot man/ cs/ 0xa9 int magic1 int magic2 unsigned int cmd void *arg
最後のvoid *argはチャレンジに書かれている"A constant string"ですね。確かにほかにintの引数が3つ必要みたいです。magic1 magic2ってなんだろう〜ということで、「reboot int magic」でググってみたところ、ばにらびいんず(鳥)さんのブログが引っかかりました。
どうやらmagic1は0xfee1dead、magic2は0x28121969ほか3つが指定されているそうです。magic2は、Linuxカーネルの父Linus Torvaldsの誕生日と娘さんたちの誕生日だそう。へえ〜〜
じゃあ次のcmdってなんだ?となります。読んでいると、
/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !kernel_can_power_off())
cmd = LINUX_REBOOT_CMD_HALT;
となっていました。
これはLINUX_REBOOT_CMD_POWER_OFFが分かればOKということで、検索したところ、linux manualのrebootのページに
LINUX_REBOOT_CMD_POWER_OFF
(RB_POWER_OFF, 0x4321fedc; since Linux 2.1.30). The
message "Power down." is printed, the system is stopped,
and all power is removed from the system, if possible. If
not preceded by a sync(2), data will be lost.
と記述がありました。
よって、全部10進数に直して(magic2はご本人の誕生日にしました)
4276215469
672274793
1126301404
これでフラグゲット。
rebootに引数があることに気がついていませんでした。安全装置みたいな感じかな。今後linuxトリビアとして披露していきたいですね。