Search This Blog

Friday, August 30, 2019

HackTheBox OneTwoSeven Writeup

Here's my writeup (and basically notes for myself in the future) for the OneTwoSeven machine, which had one of the most memorable rooting scenarios.  More of these will be posted as challenges/boxes get retired.

On our initial nmap scan, it turns out only ssh (22) and http (80) are available. From the webpage, we can receive sftp credentials, which we can use on port 22.  We also get our own webpage based on our account; after noticing that I can create and delete files from sftp, I realized that I control my own webpage.  Directly dropping in php files and then calling them from a new malicious webpage I made failed.  However, I noticed that I could use symbolic leaks to perhaps extract credentials; after all, it treats symbolically linked (I used soft links) files as text files.  Using this technique, I can extract other information and even valuable source code.  When I symbolically linked /var/www to a new folder in our web folder,  I noticed a vim swap file.  Inside, I found the following creds: ots-admin:Homesweethome1

However, those creds don't work yet.  Eventually, I symbolically linked index.php from web root (/var/www/html) and found a reference to signup.php and the following references to adminlink, following the condition if the IP is 127.0.0.1 or 104.24.0.54 (portal is on port 60080 according to source).
Additionally, the following was in signup.php:

function username() { $ip = $_SERVER['REMOTE_ADDR']; return "ots-" . substr(str_replace('=','',base64_encode(substr(md5($ip),0,8))),3); }
function password() { $ip = $_SERVER['REMOTE_ADDR']; return substr(md5($ip),0,8); }

Making this into its own php script locally, I tried testing it with 127.0.0.1 as the IP to find the creds and username (our earlier creds and username came from this too).  Here's what I received:ots-yODc2NGQ:f528764d

Using these credentials on port 22, I managed to own user.

Now, for root, I first attempted to visit the admin page, which can only be accessed from those specific IPs.  I used ssh tunneling (but sftp kept getting in the way until I disabled shell spawning and any command execution in my command): ssh -NT -L 60080:127.0.0.1:60080 ots-yODc2NGQ@onetwoseven.htb

We manage to enter in with ots-admin credentials.  Interestingly, the submit button is disabled, but you can easily enable it with classic inspect element.  However, upload does not work by default and there are rewrite rules.  Using the download one, I managed to download the source of ots-man-addon.php.

session_start(); if (!isset ($_SESSION['username'])) { header("Location: /login.php"); }; if ( strpos($_SERVER['REQUEST_URI'], '/addons/') !== false ) { die(); };
# OneTwoSeven Admin Plugin
# OTS Addon Manager
switch (true) {
# Upload addon to addons folder.
case preg_match('/\/addon-upload.php/',$_SERVER['REQUEST_URI']):
if(isset($_FILES['addon'])){
$errors= array();
$file_name = basename($_FILES['addon']['name']);
$file_size =$_FILES['addon']['size'];
$file_tmp =$_FILES['addon']['tmp_name'];
if($file_size > 20000){
$errors[]='Module too big for addon manager. Please upload manually.';
}
if(empty($errors)==true) {
move_uploaded_file($file_tmp,$file_name);
header("Location: /menu.php");
header("Content-Type: text/plain");
echo "File uploaded successfull.y";
} else {
header("Location: /menu.php");
header("Content-Type: text/plain");
echo "Error uploading the file: ";
print_r($errors);
}
}
break;
# Download addon from addons folder.
case preg_match('/\/addon-download.php/',$_SERVER['REQUEST_URI']):
if ($_GET['addon']) {
$addon_file = basename($_GET['addon']);
if ( file_exists($addon_file) ) {
header("Content-Disposition: attachment; filename=$addon_file");
header("Content-Type: text/plain");
readfile($addon_file);
} else {
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true, 404);
die();
}
}
break;
default:
echo "The addon manager must not be executed directly but only via<br>";
echo "the provided RewriteRules:<br><hr>";
echo "RewriteEngine On<br>";
echo "RewriteRule ^addon-upload.php   addons/ots-man-addon.php [L]<br>";
echo "RewriteRule ^addon-download.php addons/ots-man-addon.php [L]<br><hr>";
echo "By commenting individual RewriteRules you can disable single<br>";
echo "features (i.e. for security reasons)<br><br>";
echo "<font size='-2'>Please note: Disabling a feature through htaccess leads to 404 errors for now.</font>";
break;
}
Ah, we have to beat a few string checks check above.  This was my final payload (sent with Burp): addon-download.php?blah=/addon-upload.php

Using the pentest monkey webshell, I managed to pop a shell.  Looking at the output of sudo -l in basic enumeration, I noticed that I can run sudo apt-get update and sudo apt-get upgrade and set an HTTP proxy.  Sounds like a MITM attack!  Read this article to learn more.  I set the proxy to point to a burp listener on my end (with the listener off) and then in my /etc/hosts file, added onetwoseven.htb as 127.0.0.1.  Others used different configurations to hijack the connection.

Then, I looked through the installed packages on the box and decided to target bzip2.  In my malicious package, I had to make the dependencies and version number similar and slightly higher (for the latter).  Then, in a malicious post install script, I added a reverse shell, a bind shell, and a cat command to dump the root flag into tmp.  This was just for precautions because making the package took so long.  As for setting up the malicious server on our side for port 80, here is what I did:

Go to /var/lib/apt/lists to carefully check the formatting when needed.

I basically followed the article for the rest of the setup.  My package file looked like this:

Package: bzip2
Version: 1.0.6-8.2
Installed-Size: 184
Maintainer: Anibal Monsalve Salazar <anibal@debian.org>
Architecture: amd64
Replaces: libbz2 (<< 0.9.5d-3)
Depends: libbz2-1.0 (= 1.0.6-8.1), libc6 (>= 2.14)
Homepage: http:///packages.onetwoseven.htb
Description: high-quality block-sorting file compressor - utilities
Description-md5: 26e9d96b611ed3cf741ba7007fd4f233
Suggests: bzip2-doc
Multi-Arch: foreign
Tag: implemented-in::c, interface::commandline, role::program,
 scope::utility, use::compressing, works-with-format::TODO,
 works-with::archive, works-with::file
Section: utils
Priority: standard
Filename: /bzip2_1.0.6-8.2.deb
Size: 52424
SHA1: 8b1be1e08bc7ee54dfe6325f68d90d50d958ab94
MD5sum: 34a92f13d33e6d5a225f0e411a9d700f
SHA256: f55a84d3eac72958cc31c7f19c80aadd1c7cf6783c85aa355590ade261ea5b13
Description: high-quality block-sorting file compressor - utilities
 bzip2 is a freely available, patent free, high-quality data compressor.
 It typically compresses files to within 10% to 15% of the best available
 techniques, whilst being around twice as fast at compression and six
 times faster at decompression.
 .
 bzip2 compresses files using the Burrows-Wheeler block-sorting text
 compression algorithm, and Huffman coding.  Compression is generally
 considerably better than that achieved by more conventional
 LZ77/LZ78-based compressors, and approaches the performance of the PPM
 family of statistical compressors.
 .
 The archive file format of bzip2 (.bz2) is incompatible with that of its
 predecessor, bzip (.bz).
Homepage: http://www.bzip.org/

My releases file looked like this:
Origin: Devuan
Label: Devuan
Suite: Stable
Version: 1.0.6-8.2
Codename: ascii
Date: Mon, 21 Apr 2019 07:57:13 UTC
Architectures: amd64
MD5Sum:
 3d7bbfc40028200d27fb6fc0f51a94d8  main/binary-amd64/Packages
 943dba7597825e10c8598835e0324a3f  main/binary-amd64/Packages.gz
SHA1:
 97d6e52da5befcd8a0b082bd9ab48195d00109d8 main/binary-amd64/Packages
 ef45d09148383a24aaeba1b8ba246b9c50ad5416 main/binary-amd64/Packages.gz
SHA256:
 c764a146c77ef8bbd7978fa5841fcd2243477eb9f79dc84bac2bc7f7662aab11 main/binary-amd64/Packages
 cb444bd2cbd541835c81cbdeaef1594f64dc58d1c66afa97ff1914193526dc28 main/binary-amd64/Packages.gz

Then, from a few failed attempts, I slowly figured out the correct directory structure.  Devuan was the first directory, bzip package was contained in a pool folder, and then under dists/ascii, I held the Release and Release.gz files.  Within here, I had a binary-amd64 folder under main, and the Packages and Packages.gz files in there.  I also held all these files in the same the malicious server root too.  Now, I managed to get a shell back, and this box was owned!

Saturday, August 24, 2019

Some Interesting ROP Techniques

Besides some heap pwning, I've also been playing around with many interesting ROP techniques I have seen from the many new stack problems I have tried.  Previously, I usually relied on syscalls or a ret2libc for the majority of my rop attacks, but now as I encounter more difficult challenges, I have discovered some cool techniques.  I will be discussing some of them here.

1. ret2csu

Sometimes, you can't seem to find any gadgets at all as arguments for a certain function you want to make in the program on a 64 bit pwn problem.  In this scenario, ret2csu comes in handy.  I only encountered this in the final problem of ROP Emporium.  Anyways, ELF binaries can have this: __libc_csu_init.  All the 3 main gadgets for calling conventions (rdi, rsi, rdx) are supposed to be there, although misalignment might be necessary sometimes.  However, returns aren't exactly there immediately afterwards... some program logic will run through with other registers so those need to be set accordingly for the exploit to work.  Most importantly, this instruction probably is there: call QWORD PTR [r12+rbx*8].  If not controlled correctly, a segfault will occur.  It also must not disturb the state of the other registers we have to control.  A call to __init or __fini should be fine in this scenario and your exploit should work.

2. ret2mprotect

We use ROP chains in stack smashing to beat NX.  However, what if allow regions of non-exectuable memory to become executable again?  That way, we can shellcode once again!  My first encounter of this was from HackTheBox's Calamity Box.  Basically, we just need to control three arguments of mprotect: the address of mapped memory, the size, and the flag.  0x7 for the flag makes that region of memory executable.  I usually set that to the entire region of the stack/heap depending which one I am going for.  Then, have the vulnerable program return to shellcode and it will execute!

3. ret2dlresolve

Basically, use this technique when you have no output and no idea about the libc.  This technique was quite difficult for me to learn and I might not provide the best explanation (I learned from these links: link 1, link 2, link 3).  __dl_runtime_resolve is called to resolve addresses of a function when it is first called in a dynamic binary while also updating GOT.  When calling it, the binary passes an argument of the index in the relocation table and a pointer to something called link_map, which is a linked list of loaded libraries.  The entry in the relocation table leads to an index in the symbol table, which leads to an index in the string table regarding the name.  Some additional information before continuing: JMPREL is stores the relocation table, in which each entry corresponds with a symbol, STRTAB stores the strings for the symbols, and SYMTAB holds information for symbols.
JMPREL has this structure:
typedef uint32_t Elf32_Addr ;
 typedef uint32_t Elf32_Word ;
 typedef struct {
 Elf32_Addr r_offset ; /* Address */
 Elf32_Word r_info ; /* Relocation type and symbol index */
 } Elf32_Rel ; //this is the type of entry in this segment
 #define ELF32_R_SYM(val) ((val) >> 8)
 #define ELF32_R_TYPE(val) ((val) & 0xff)

Only relevant field in SYMTAB is the first thing defined in the struct: st_name.  This tells the program the offset of the string in STRTAB.  We can probably craft fake structures in regions of memory where we control (fake entries of JMPREL and SYM structures).  Using dl resolve, which takes the link map and an offset to Elf32_Rel in JMPREL, we can pwn this binary.  No bounds checking is done on the offset; this will allow us to redirect it to our fake structures.  Using this idea, we can force it to give us a nice system(), or any other function; this function must be set up as an argument as the resolver calls this afterwards.  Our fake structures should be like this:
1. make r_offset somewhere writable (normally somewhere in GOT)
2. make the high 24 bits of r_info point to the fake structure for sym elsewhere
3. make the last 8 bits of r_info equal to 0x7 (for this to work, this is necessary)
4. address of the system string (add st_name to STRTAB)
5. MAKE SURE TO HAVE SYSTEM PREPARED ALREADY WITH A NICE /BIN/SH

And there you have it... system pwned!

Wednesday, August 21, 2019

Heap Exploitation Part 2 (House of Force, House of Spirit, Unsorted Bin, and the Family of Off By Ones)



It was a while since heap exploitation part 1, in which I detailed fastbin attack, double frees, and UAF.  Today, I will delve a much deeper into some slightly more difficult techniques... make sure you know how glibc malloc and free works at a detailed level before continuing!  Additionally, these are more summaries and ideas of what is going on rather than full on write ups of specific problems.  Let us begin!

1. House of Force

This technique is quite old, but still works on many CTF challenges.  Basically, if you can overflow the heap and control allocation sizes, you can achieve arbitrary write and probably pop a shell.  You can also find examples of such exploits from HitConCTF and BackdoorCTF... I am currently writing a challenge related to this for another CTF.
Generally, if you can overflow and drastically change the size of the top chunk; most typical examples show exploits overwriting the top chunk with -1, which becomes a very large value in memory.  By doing this, the size field of the top chunk changes from the usual 0x21000 (or something smaller after a few allocations) to 0xFFFFFFFFFFFFFFFF, which can basically cover the entire program's memory (so no more mmap calls!).  Now let's say you want to get address X to mess with... allocating the difference between the address of the target and the address of the top chunk (in addition to the 16 bytes of metadata on 64 bit computers and perhaps an extra few bytes to correct malloc rounding), you will get that region to overwrite!  Here's the way I perform my calculations in these problems (64 bit):

request = c_ulong(target).value - 8*5 - c_ulong(topchunk).value
request = c_ulong(request).value

Very cool technique indeed!

2. House of Spirit

I honestly just figured this out from this old CTF challenge: 0x00 2017 Spiritual Memos.  If you can get a stack leak or a heap leak, you can then attempt to overwrite a pointer to point to a location near the stack or heap region (or other places too) you have control over (remember to take into consideration 0x10 bytes of metadata on 64 bit machines).  Make sure to forge your chunk correctly though and make sure that the data around it will allow it to pass the next size check.  Then, you can free that pointer (I prefer fastbin sized chunks), and once you get that back, you can start overwriting!  In that old CTF challenge I mentioned (I won't discuss it in too much detail here), there was basically an overflow bug over a buffer and the pointer right next to it (including a canary also); thankfully, the usage of read (which lacks the attachment of a null byte) allowed us to see a lot of data, including the canary as well as another pointer that was always at a constant offset from the stack (probably something leftover from libc initialization or some other startup function).  The same concept was used for the libc leak.  Then, with all these leaks gathered nicely, I used the same overflow capabilities to help me with the creation of a fake chunk while setting the pointer equal to it.  This time, I made sure that the size was large enough to help me overwrite the return address of the function.  Hence, when I freed and malloc again, I get back this fake chunk, and achieved arbitrary write, using a one gadget to pop a shell.

3. Unsorted Bin Attack

This technique isn't often used for RCE or popping shells.  Rather, it usually is useful to overwrite another variable elsewhere and further the path for exploitation.  How2Heap does a great job explaining this attack.  Basically, you need to be able to malloc multiple chunks bigger than fastbin size (you should never free the last one because of top chunk consolidation); if you overwrite the bk pointer of the freed victim chunk (take into account the 16 bytes of metadata!) in unsorted bin, upon freeing it, the victim address will get updated to the address of the unsorted bin, due to how malloc/glibc works.

4. Family of Off by Ones.  I consider this a pretty important, if not one of the most important, categories.  There are many types here, but they are all very similar: House of Einherjar, Poison Null Byte, etc.
Basically, if you get a one byte overflow (from issues like strlen() vs read(), adding 1 to the amount to reading... there's a ton of examples out there) and have some degree of control over allocation sizes, you can actually get arbitrary write.  The 0x00sec article on the poison null byte actually documents this very well in my opinion.  What is the end goal?  To  overlap a newly allocated chunk with an already in use chunk to overwrite the latter's metadata pointers.  For example, if you can make a chunk with the last line (in heap memory of gdb) with a fake but slightly smaller chunk prev size data (for beating a check in the unlink macro later on), and have a chunk above, and two chunks below (the last one for preventing top consolidation), you can start attacking!  By freeing the second chunk, the set foot macro will not affect the fake prev_size we created above (make sure these chunks are all not fastbin sized).  The third chunk's prev in use bit will be unset. Then, overflow from chunk #1 to shrink the second chunk to match the fake prev size value.  Then we can allocate another chunk there (a few chunks usually... one to prepare for back coalescing and the others are fastbins for the final part of the attack).  Now, when you free newly created non-fastbin chunk and then the third chunk (and most likely the other ones to help with the attack later when performing arbitrary write), back coalescing will occur and you will get access to a huge region of overlapped chunks.  You can now overwrite parts of the fastbin carefully and get arbitrary write!  Target wherever you desire... I usually go for the BSS section of the binary if it is possible, as it usually stores the array that stores the pointers to the chunks... I can overwrite those and then manipulate them with the alloc, edit, delete, etc. functions many CTF problems provide.

5. Poison Null Byte
Basically the same idea as off by ones.  Just be more careful about the way you craft everything because you can only overwrite with a null byte.

Happy pwning guys!  Come back soon for part 3!