Qualify for semi finals! Here is some writeups for the challenges
Challenges
brote (rev)
Challenge file:
We are given a python script
Try run it in terminal:
python3 brote.py
Enter flag for verification: a
Bad flag!
Look at the source code:
import re
import hashlib
def md5_sha1(x):
a = hashlib.sha1(x).hexdigest().encode('utf-8')
return hashlib.md5(a)
def md5_md5_md5(x):
a = hashlib.md5(x).hexdigest().encode('utf-8')
a = hashlib.md5(a).hexdigest().encode('utf-8')
return hashlib.md5(a)
x = [hashlib.sha224, hashlib.sha256, hashlib.sha384, hashlib.sha512, hashlib.md5, hashlib.sha1, md5_sha1, md5_md5_md5]
y = ['3770f06168c0137f3471ba682068ba1de460468d3f04a6412c23fa59', '1d853c1e3b909efceb14a4d92337589041372449d2edc83f890b5fde31d3644b', 'e85fb4a65bc3613d33910d9836cddad339e82a9c3f773c2b23d6bb9734431251ba3e9900e7e22dd5df0f575977549147', '9d7108c35983cf0f0789ed4b09bf44f5b089dc2b9c770336d71fe123908f782318c48c2849dc59c7ad3be9ade3f568bb1cf7e42c3dd9795de829565d46343ac6', '2fc2e03201e06a52b59cdf57075336d8', 'f5043ed6f522cd05f3a771d471ba82f4945feab5', '6ef11bc47eadba4c9c4c44f1e4aaa84f', '3850cda130224d889937e2c652bad56c'
]
def check_flag(flag):
flag = flag.encode()
chunks = [flag[i:i+4] for i in range(0, len(flag), 4)]
for c,(f,o) in zip(chunks, zip(x,y)):
if f(c).hexdigest() != o:
return False
return True
user_input = input("Enter flag for verification: ")
if user_input and \
len(user_input) == 32 and \
re.match(r'^[a-z0-9]+$', user_input) and \
check_flag(user_input):
print("Congratulations!")
else:
print("Bad flag!")
We can see our input is the flag, and the correct flag should be:
- In length of 32
- lowercase letter or number
- return true for
check_flag
Let’s see what check_flag
function does:
def check_flag(flag):
flag = flag.encode()
chunks = [flag[i:i+4] for i in range(0, len(flag), 4)]
for c,(f,o) in zip(chunks, zip(x,y)):
if f(c).hexdigest() != o:
return False
return True
We can see it split the flag
in four chunks, then it pass into a for loop
The for loop is just looping for hash functions in x
is it equal to hex values y
You can see the x
and y
array:
x = [hashlib.sha224, hashlib.sha256, hashlib.sha384, hashlib.sha512, hashlib.md5, hashlib.sha1, md5_sha1, md5_md5_md5]
y = ['3770f06168c0137f3471ba682068ba1de460468d3f04a6412c23fa59', '1d853c1e3b909efceb14a4d92337589041372449d2edc83f890b5fde31d3644b', 'e85fb4a65bc3613d33910d9836cddad339e82a9c3f773c2b23d6bb9734431251ba3e9900e7e22dd5df0f575977549147', '9d7108c35983cf0f0789ed4b09bf44f5b089dc2b9c770336d71fe123908f782318c48c2849dc59c7ad3be9ade3f568bb1cf7e42c3dd9795de829565d46343ac6', '2fc2e03201e06a52b59cdf57075336d8', 'f5043ed6f522cd05f3a771d471ba82f4945feab5', '6ef11bc47eadba4c9c4c44f1e4aaa84f', '3850cda130224d889937e2c652bad56c'
]
We know that hash functions cannot reverse but we can brute force it!
Therefore, to solve this we must brute force the flag but in 4 character at a time
So quite fast to solve this
Solving
Write a simple python script, I use pwntools the brute force the flag:
flag = ''
for i in range(len(x)):
flag += pwnlib.util.iters.mbruteforce(lambda text: x[i](text.encode()).hexdigest() == y[i], string.ascii_lowercase+string.digits, length = 4)
print(flag)
# [+] MBruteforcing: Found key: "fsbr"
# [+] MBruteforcing: Found key: "34k4"
# [+] MBruteforcing: Found key: "llth"
# [+] MBruteforcing: Found key: "3s3h"
# [+] MBruteforcing: Found key: "4sh3"
# [+] MBruteforcing: Found key: "spl3"
# [+] MBruteforcing: Found key: "453c"
# [+] MBruteforcing: Found key: "yber"
# fsbr34k4llth3s3h4sh3spl3453cyber
Thats it! Simple reverse challenge! Full python script
Crypto Stream
Challenge files
We are given a python script encrypt.py
and a binary file flag.enc
Let’s see the source code:
import random
from secret import flag, key
def generate_key(s):
assert len(s) == 16, "The key must be 16 bytes long"
k = [0, 0]
for i in range(len(s)):
if i == 0:
k[0] = k[0] ^ s[i]
k[1] = (k[1] + s[i]) & 0xFF
else:
k[0] = (k[0] ^ (s[i] - s[i - 1])) & 0xFF
k[1] = (k[1] + s[i] ^ s[i - 1]) & 0xFF
return k
def encrypt(s, k):
n1 = k[0]
n2 = k[1]
encrypted = []
for c in s:
encrypted.append(c ^ n1)
random.seed(n1)
n1 = n2
n2 = random.randint(0, 255)
return bytes(encrypted).hex()
e = encrypt(flag, generate_key(key))
with open('flag.enc', 'w') as f:
f.write(e)
As you can see, it encrypts the flag
with a key
that we don’t know
Then save the encrypted flag inside flag.enc
First bug
Notice that the generate_key
function return array k
and the value must be <= 255
Because it perform AND bitwise operation, therefore the generated key must be <= 255
Second bug
We can predict the value of n1
and n2
inside the encrypt
function
Because it use n1
as the random seed, that means if we know the correct n1
we can predict n2
and the entire flag!!
Solving (Brute force)
My idea is to brute force all 256 values for k[0]
and k[1]
, until it decrypt the flag starts with fs
and ends with cyber
enc = binascii.unhexlify(open("flag.enc",'rb').read())
for k1 in range(256):
for k2 in range(256):
flag = decrypt(f,[k1,k2]) # decrypt is same as encrypt
if flag.startswith(b"fs") and flag.endswith(b"cyber"):
print(k1,k2)
print(flag)
# 40 186
# b'fsKn0wnPl41NTextA77ackcyber'
We can see the key is 40 and 186, and we get the decrypted flag!!
Solving (Decrypt)
Another method is to calculate the key using the flag format
n1 = k[0]
, n2 = k[1]
and it encrypt the flag using c ^ n1
To find n1
and n2
, we can XOR the character of encrypted flag and fs
We know the flag format starts with fs
, therefore to find k1
we can calculate f XOR enc_flag[0]
, to find k2
calculate s XOR enc_flag[1]
Write in Python script:
enc = binascii.unhexlify(open("flag.enc",'rb').read())
k = [ord('f') ^ enc[0], ord('s') ^ enc[1]]
print(k)
print(decrypt(enc,k))
# [40, 186]
# b'fsKn0wnPl41NTextA77ackcyber'
As you can see, we get the same key and same flag!
Monalisa
Challenge files
We are given a PCAP file (Packet capture file)
Lets open it with Wireshark
There are many packets in the file..
We can filter the content by enter http
in the filter and enter:
Can see there are many stuff like html file, style css etc.
We can export the objects by going File > Export Objects > HTTP
After that click Save All to a folder
Analyze
Saw a file called client.js
seems interesting..
document.addEventListener("DOMContentLoaded", function() {
function x() {
return Math.floor(100 * Math.random()) + 1
}
var n = {
click: !1,
move: !1,
pos: {
x: 0,
y: 0
},
pos_prev: !1
},
e = document.getElementById("board"),
t = e.getContext("2d"),
i = window.innerWidth,
c = window.innerHeight,
r = io.connect();
e.width = i, e.height = c, e.onmousedown = function(o) {
n.click = !0
}, e.onmouseup = function(o) {
n.click = !1
}, e.onmousemove = function(e) {
n.pos.x = e.clientX / i + x(), n.pos.y = e.clientY / c + x(), n.move = !0
}, r.on("coor", function(o) {
var n = o.line,
x1 = (n[0].x - Math.floor(n[0].x)) * i,
y1 = (n[0].y - Math.floor(n[0].y)) * c,
x2 = (n[1].x - Math.floor(n[1].x)) * i,
y2 = (n[1].y - Math.floor(n[1].y)) * c;
t.beginPath(), t.moveTo(x1, y1), t.lineTo(x2, y2), t.stroke()
}),
function o() {
n.click && n.move && n.pos_prev && (r.emit("coor", {
line: [n.pos, n.pos_prev]
}), n.move = !1), n.pos_prev = {
x: n.pos.x,
y: n.pos.y
}, setTimeout(o, 25)
}()
});
Seems like it is sending some x and y coordinates..
Also saw the HTML file:
<!DOCTYPE html>
<html>
<head>
<title>monalisa</title>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css" />
<script src="socket.io/socket.io.js"></script>
<script src="client.js"></script>
</head>
<body>
<canvas id="board"></canvas>
</body>
</html>
The challenge name called Monalisa, so we can guess it is drawing the flag, and the draw coordinates was recorded in this packet capture file
Lets try to find the coordinates!
After finding in the wireshark, finally I found the coordinates in WebSocket!
We can see the whole coordinates Right Click > Follow > TCP Stream
Then it will show all coordinates sent:
Click Save as
to save as a file
Solving
We need to investigate how to recover the “drawing”
By searching Javascript drawing
can see alot of tutorials like this one
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.moveTo(0,0);
ctx.lineTo(200,100);
ctx.stroke();
Is simple, moveTo
is the starting point and lineTo
is the ending point
Therefore we have to gather all coordinates in the capture file and then process it in this script!
We can use grep
command in Bash to grab only the coordinates of the file:
grep -Po '\[{"x":[0-9]+\.[0-9]+,"y":[0-9]+\.[0-9]+},{"x":[0-9]+\.[0-9]+,"y":[0-9]+\.[0-9]+}\]' coordinates | while read line;do echo $line,;done
Alternatively, can use python script:
import re
f = open("coordinates",'r').read()
coor = re.findall(r'\[{"x":[0-9]+\.[0-9]+,"y":[0-9]+\.[0-9]+},{"x":[0-9]+\.[0-9]+,"y":[0-9]+\.[0-9]+}\]', f)
for c in coor:
print(c,end=',\n')
I use the HTML template on W3school:
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="200" height="100"
style="border:1px solid #d3d3d3;">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.moveTo(0,0);
ctx.lineTo(200,100);
ctx.stroke();
</script>
</body>
</html>
Copy the coordinates into the script:
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas"
style="border:1px solid #d3d3d3;">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var coor = [
[{"x":23.06351665375678,"y":74.46076794657763},{"x":23.06351665375678,"y":74.46076794657763}],
...
...
...
]
for (var i = 0; i < coor.length; i++) {
x1 = coor[i][0]["x"];
y1 = coor[i][0]["y"];
x2 = coor[i][1]["x"];
y2 = coor[i][1]["y"];
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
}
</script>
</body>
</html>
Open it in browser, it will show the flag (if not mistaken..)
Its not like flag or Monalisa…
Look back the client.js
:
function x() {
return Math.floor(100 * Math.random()) + 1
}
...
...
e.onmousemove = function(e) {
n.pos.x = e.clientX / i + x(), n.pos.y = e.clientY / c + x(), n.move = !0
},
Notice it got add some random values in the coordinates! But how we gonna guess the coordinate if we don’t know the flag??
Look carefully in how it draw the lines:
i = window.innerWidth,
c = window.innerHeight,
...
...
r.on("coor", function(o) {
var n = o.line,
x1 = (n[0].x - Math.floor(n[0].x)) * i,
y1 = (n[0].y - Math.floor(n[0].y)) * c,
x2 = (n[1].x - Math.floor(n[1].x)) * i,
y2 = (n[1].y - Math.floor(n[1].y)) * c;
t.beginPath(), t.moveTo(x1, y1), t.lineTo(x2, y2), t.stroke()
})
It takes the coordinates minus the integer part left only decimal part (minus Math.floor) then multiply by width or height
That means we don’t even need to guess the random number, because we only need the decimal part and integer part (random number) is not important!
Added some code to minus and multiply the width and height:
var w = window.innerWidth;
var h = window.innerHeight;
canvas.width = w;
canvas.height = h;
for (var i = 0; i < coor.length; i++) {
x1 = (coor[i][0]["x"] - Math.floor(coor[i][0]["x"])) * w;
y1 = (coor[i][0]["y"] - Math.floor(coor[i][0]["y"])) * h;
x2 = (coor[i][1]["x"] - Math.floor(coor[i][1]["x"])) * w;
y2 = (coor[i][1]["y"] - Math.floor(coor[i][1]["y"])) * h;
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
}
It draw us the Monalisa!! Full HTML script
Interesting and fun forensics challenge!
Web
Challenge file:
We are given a HTML file, lets look at it:
<script>
var url_string=window['location']['href'],url=new URL(url_string),p=url['searchParams']['get']('p');function ebg13(_0x394e9e){var _0x1bad7e='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',_0xe7ba36='NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm',_0x178280=_0x4299b3=>_0x1bad7e['indexOf'](_0x4299b3),_0x4e7551=_0x228b79=>_0x178280(_0x228b79)>-0x1?_0xe7ba36[_0x178280(_0x228b79)]:_0x228b79;return
...
...
...
</script>
Looks like obfuscated.. lets beautify it (Can use online Javascript Beautifier)
I use sublime text package for Javascript Beautify
Analyze
At the start of the script we can see it get the parameter p
from the URL:
var url_string = window['location']['href'],
url = new URL(url_string),
p = url['searchParams']['get']('p');
Then at the end of script it check the p
is length of 0x18 (24) and pass to check
function:
p != null && p['length'] == 0x18 && check(p);
Can see I test added p
parameter in the URL:
Looks like p
is the password and also flag
Lets look into check
function:
function check(_0x1c2dbb) {
var _0x48f7e1 = [aa, bb, cc];
for (var _0x4b521e = 0x0; _0x4b521e < _0x48f7e1['length']; _0x4b521e++) {
var _0x1fd69e = _0x48f7e1[_0x4b521e](_0x1c2dbb);
if (!_0x1fd69e) {
alert('Not\x20quite\x20right');
return;
}
}
alert('Accepted\x20:)');
}
It looks messy! let me clean up abit to see whats going on:
function check(flag) {
var func = [aa, bb, cc];
for (var i = 0x0; i < func['length']; i++) {
var correct = func[i](flag);
if (!correct) {
alert('Not\x20quite\x20right');
return;
}
}
alert('Accepted\x20:)');
}
Now we can see clearly, it just pass our password (flag) into 3 functions aa
bb
cc
Then if one of them return false
then our flag is not correct!
First part (Sha256)
Look into function aa
:
function aa(_0x23c1f1) {
var _0xbdcdeb = ['dce7cce055566bed799f788cd0048e209a27a473c0f48b956fa1f1780e80d2c1', '635ca73d00d4f28b5f573b16eea56e9e4579d77e561c32aa68189d9769fa1753', 'a4d0ef23161b5b7c6a8d5b287543fd74e16b3bf313d71aa187c24cdd728a7b1e', 'e0b9a8799f32453a478c9122f8b83cee68e16db18f493ac81bc1d474594b5df4'],
_0x39b271 = _0x23c1f1['substring'](0x0, 0x8)['match'](/.{1,2}/g);
for (var _0x38d68b = 0x0; _0x38d68b < _0x39b271['length']; _0x38d68b++) {
var _0x5169a8 = ansdweasd(_0x39b271[_0x38d68b]);
if (_0x5169a8 != _0xbdcdeb[_0x38d68b]) return ![];
}
return !![];
}
Hmm.. the long string in the variable seems like hash, lets try to crack it in crackstation
It is a hash! Is Sha256 and it cracked the flag format fs
After cracking all 4 hashes, we can get the part of flag fs0bfusc
Can use it running ['substring'](0x0, 0x8)
with our flag (1st character to 8th character) so total 8 characters
Second part (ROT13)
We will look at function cc
first:
function cc(_0x159c99) {
var _0x593622 = ebg13(_0x159c99['substring'](0x10, 0x18));
return _0x593622 == '4egplore';
}
Can see it pass the last 8 characters of the flag to function ebg13
:
function ebg13(_0x394e9e) {
var _0x1bad7e = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
_0xe7ba36 = 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm',
_0x178280 = _0x4299b3 => _0x1bad7e['indexOf'](_0x4299b3),
_0x4e7551 = _0x228b79 => _0x178280(_0x228b79) > -0x1 ? _0xe7ba36[_0x178280(_0x228b79)] : _0x228b79;
return _0x394e9e['split']('')['map'](_0x4e7551)['join']('');
}
This function looks like doing some mapping of alphabet, we can guess this is doing the famous encryption ROT13!! The function name also hint it
Therefore, we just have to decrypt 4egplore
using ROT13, it is
4rtcyber
Third part (XOR)
Now we look at function bb
:
function bb(_0x507300) {
var _0x3360b0 = _0x507300['substring'](0x0, 0x2) + _0x507300['substring'](0x14, 0x16),
_0x5415bb = _0x507300['substring'](0x8, 0x10),
_0x244a1b = [0x52, 0x7, 0x4a, 0x6, 0x4, 0xa, 0x0, 0x12];
for (var _0x58422e = 0x0; _0x58422e < _0x5415bb['length']; ++_0x58422e) {
var _0x2e8721 = _0x3360b0[_0x58422e % _0x3360b0['length']]['charCodeAt'](0x0),
_0x40bbc5 = _0x5415bb[_0x58422e]['charCodeAt'](0x0);
if (_0x244a1b[_0x58422e] != (_0x2e8721 ^ _0x40bbc5)) return ![];
}
return !![];
}
Looks messy also clean it up:
function bb(flag) {
var f1 = flag['substring'](0x0, 0x2) + flag['substring'](0x14, 0x16),
f2 = flag['substring'](0x8, 0x10),
verify = [0x52, 0x7, 0x4a, 0x6, 0x4, 0xa, 0x0, 0x12];
for (var i = 0x0; i < f2['length']; ++i) {
var a = f1[i % f1['length']]['charCodeAt'](0x0),
b = f2[i]['charCodeAt'](0x0);
if (verify[i] != (a ^ b)) return ![];
}
return !![];
}
Ok look much better..
Can see it perform some XOR operation, we know that XOR operation can be reverse easily
Like A XOR B = C
then, A = C XOR B
, B = C XOR A
We know the first and last 8 characters of the flag, which means we know what is f1
, can see the code f1
is XOR with f2
Therefore, we can calculate middle part of flag (which is f2
) based on f1
and the verify
variable!
Write a simple python script to calculate this:
flag = bytearray("fs0bfusc????????4rtcyber",encoding="ascii")
f1 = flag[0:2] + flag[0x14:0x16]
verify = [0x52, 0x7, 0x4a, 0x6, 0x4, 0xa, 0x0, 0x12]
for i in range(8):
flag[i+8] = verify[i] ^ f1[i%4]
print(flag)
# bytearray(b'fs0bfusc4t3dbyyp4rtcyber')
We made it! We get the whole flag! Lets test it in the URL:
Thats it simple JS reversing!