Real Sec CTF

I think CTFs are great but sometimes the challenges can feel a bit made up (LiveOverflow has great video on this! CTFs are TERRIBLE!). Therefore I made "Real Sec CTF" where all the challenges are based on real code that was used in production! The vulnerabilities presented here are all reported, fixed or not in use anymore. I plan to add my own write-ups to these but right now there are no solutions published on this page.

If you have any interesting security vulnerabilities that you've found in the wild and want to share, just let me know!

2022-08-20 - Now we have a scoreboard! :) The order for people with the same number of solves is arbitrary.

2022-03-25 - Wow! Great job Q on solving all the original seven challenges!

Scoreboard

Username Solves
ludde12
Q12
PotatoKingTheVII12
corgo12
Peptski12
har-ee11
Assured10
TITANPOINTE10
φ9
Intel® HD Graphics8

Challenges

1. A secure client-side login?

Trying to write a login with only JavaScript, without any communication to server-side code, might seem impossible. Anyone can inspect the code to check the password or simply jump to the "successful" login function. However, I think this solution was pretty clever!

function login() {
  var pw = document.getElementById('login').value;
  var prod = 1;
  for(var i = 0; i < pw.length; i++) {
    prod *= pw.charCodeAt(i);
  }
  
  if( prod == 19811137522176000 ) {
    window.location = pw + ".php";
  }
}

Only if prod matches a seemingly magic number will we jump to the correct PHP file.

Update 2021-02-09: Thanks to GoldFish for finding the original article (also mentioned here) that explains how to implement this "encryption". I've changed the password to mitigate searching for the answer, the password strength remains the same. I should add that when I first found this "encryption" it was used on a live production website!

Your username:
Flag:

Solvers:

Username Date
GoldFish2021-02-08 20:00:12
Dubrefjord2021-05-12 09:01:33
tihanyin2021-06-09 19:51:10
grocid2021-08-28 18:24:19
yoda2022-03-21 14:35:52
Peptski2022-03-21 16:42:55
DZ2022-03-21 17:34:21
Q2022-03-21 21:10:29
φ2022-03-22 17:51:49
saga2022-03-24 01:17:33
dani2022-03-24 01:19:31
<script>alert("Intel® HD Graphics")</script>2022-03-26 20:49:28
nightmorning2022-03-28 21:45:47
1922022-03-29 15:59:10
Intel® HD Graphics2022-04-01 19:24:37
Assured2022-04-28 22:27:33
Steget2022-05-11 11:12:12
dab2022-05-11 11:12:14
PotatoKingTheVII2022-06-17 14:14:35
TITANPOINTE2022-08-19 09:04:51
corgo2022-08-29 22:31:57
7272022-09-21 21:11:17
har-ee2022-11-03 00:14:35
Grybto2022-11-29 15:07:50
epistemologist2023-01-04 23:43:33
ludde2023-03-20 11:58:37
shyhungalpha2023-03-20 21:40:12
alax2023-04-02 08:10:57
PeterPan2023-04-04 15:04:04

2. Double hashing for extra security!

This is something I found in a PHP malware, a shell to be specific. Attackers would upload this to a vulnerable server giving them remote code execution capabilities. To protect their shell from other attackers they have a password check and like true security experts they store the password hashed, or at least part of it? Hmm...
They also know that MD5 is weak so they add an extra layer of SHA1.

if(substr(sha1(md5($_POST['pw'])),36)=='222f') {
  system($_POST['code']);
}

If you think this was easy you can head over to Hash Game for some tougher hashing challenges. For more on PHP shells you can checkout my blog post Improving web shells with asymmetric encryption .

Your username:
Flag:

Solvers:

Username Date
GoldFish2021-02-08 18:05:07
SimonGoater2021-04-07 16:54:39
Dubrefjord2021-05-12 13:13:08
tihanyin2021-06-09 20:04:28
grocid2021-08-28 18:32:54
caark2022-03-17 04:03:53
Q2022-03-22 10:15:34
Peptski2022-03-22 10:18:29
φ2022-03-22 18:25:58
yoda2022-03-25 17:48:38
dani2022-03-25 18:46:43
Intel® HD Graphics2022-03-26 21:06:15
nightmorning2022-03-28 23:09:48
Assured2022-04-28 22:48:47
dab2022-05-11 11:38:01
Steget2022-05-11 11:43:07
PotatoKingTheVII2022-06-17 14:30:19
TITANPOINTE2022-08-18 21:05:44
corgo2022-08-29 22:49:00
go2022-09-06 03:01:22
7272022-09-21 21:12:06
har-ee2022-11-02 22:03:15
Grybto2022-11-29 15:42:33
epistemologist2023-01-05 00:02:12
ludde2023-03-20 12:05:41
shyhungalpha2023-03-20 21:59:35
alax2023-04-03 12:22:17
PeterPan2023-04-13 11:04:19
omstaendlig2024-03-03 08:17:59

3. I'll roll my own crypto!

Crypto people are really smart, no surprise, but they are also very business savvy. As soon as someone comes up with a brilliant crypto system they scream "Don't roll your own crypto!", keeping the market to themselves.

"No more!" said this developer and rolled their own crypto. Are the crypto people right again? Can you crack it?

It was all packed nicely as HTML+JavaScript with a nice popup for the password: denisa.html (The code does not initialize any external network communication)

Below is a snippet of the crypto code responsible for the decryption. It's not enough to solve the challenge but might present some weaknesses.

passnum = orig.length % password.length;
for(i=orig.length-1; i>=0; i--) {
  passnum--;
  if (passnum == -1) passnum = password.length - 1;

  pos1 = i;
  pos2 = i + password[passnum];

  if (pos2 >= orig.length) continue;

  char1 = orig[pos1];
  char2 = orig[pos2];

  orig[pos2] = char1;
  orig[pos1] = char2;
}

orig1 = "";
for(i=0;i<orig.length;i++) {
  orig1 = orig1 + orig[i];
}
orig1 = orig1.replace(/mmm/g,"\r\n");
Your username:
Flag:

Solvers:

Username Date
Q2022-03-22 15:42:04
Peptski2022-03-26 16:24:59
φ2022-03-26 23:32:29
Intel® HD Graphics2022-03-29 13:41:45
Assured2022-05-14 20:30:09
PotatoKingTheVII2022-06-17 21:00:17
ludde2023-03-20 12:42:26
Corgo2023-04-24 00:56:39

4. Time to score!

You're playing a really hard flappy bird JavaScript clone and you really want to impress your crush with a nice high score. However, you are terrible at the game. Luckily you are better at reverse engineering and cryptography!

At the end of the game, the client sends three values to the server, score, time and the key, where key is calculated as:

key = md5(secretString + score + time) 

The secretString is a shared secret between the client and server. But due to some amazing obfuscation work, you do not know the value of secretString. You can further assume that secretString is a long random string that cannot be bruteforced.

First time you played the game it took over 5 minutes (345 seconds) to get a score of 12. Resulting in the following request:

  • score: 12
  • time: 345
  • key: 6e1098c92fb8ad01face64ffd8b8b025

Can you modify the request to get over 100 points?

Your username:
Score:
Time:
Key:

Solvers:

Username Date
grocid2021-08-28 18:34:10
GoldFish2021-11-20 15:54:43
Peptski2022-03-21 18:07:17
Q2022-03-22 11:32:13
φ2022-03-23 18:17:41
Intel® HD Graphics2022-03-28 15:55:48
nightmorning2022-03-29 07:34:32
Assured2022-05-09 23:07:35
PotatoKingTheVII2022-06-17 15:45:49
TITANPOINTE2022-08-22 16:30:11
h2022-11-02 02:07:58
har-ee2022-11-02 21:47:25
ludde2023-03-20 12:43:20
monk2023-03-22 03:26:54
divisionby02023-03-22 15:02:55
alax2023-04-03 12:37:43
Corgo2023-04-24 00:00:48
Mary P0ppins2023-05-27 15:26:18
Simon Goater2023-07-22 12:37:47

5. No .php files here!

Picking a good photo for your dating profile is tricky. You need to think about lightning, background, etc. But perhaps most important is the file extension. A classic .jpg? A transparent .png? Or maybe a funny .gif? Perhaps a .php file? 😈

Well, this site only allows a few extensions, as can be seen in the validation code below:

$filename = basename($_FILES["file"]["name"]);
$ext = explode(".", $filename)[1];

if( !($ext == "jpg" || $ext == "png" || $ext == "gif") ) {
  die("Only jpg, png and gif are allowed!");
}

Can you upload a file with the .php file extension, that also executes the function win();? I.e. the file should contain <?php win(); ?>.

Your username:
File:

Solvers:

Username Date
SimonGoater2021-07-16 13:18:19
Augmenta<script>alert("your solution for challenge 1 is flawed, it should accept all typable characters. Also, stored XSS lol.")</script>2021-09-06 03:35:44
x2021-09-06 04:30:36
Goldfish2021-11-20 15:58:57
Peptski2022-03-21 18:09:44
Q2022-03-22 11:35:45
φ2022-03-23 18:54:45
Intel® HD Graphics2022-03-28 15:58:23
nightmorning2022-03-29 07:39:01
Koftan2022-03-31 21:51:42
ph.mikey2022-04-15 21:52:06
Assured2022-05-09 23:40:56
GreenPenguin2022-05-11 11:58:38
Steget2022-05-11 12:00:12
dab2022-05-11 12:38:27
PotatoKingTheVII2022-06-17 14:37:46
TITANPOINTE2022-08-18 21:20:17
corgo2022-08-29 22:52:20
7272022-09-21 21:13:03
har-ee2022-11-02 21:48:49
Grybto2022-12-01 12:57:21
ludde2023-03-20 12:51:12
Nonzero2023-03-20 14:33:21
shyhungalpha2023-03-20 22:22:40
AT2023-03-23 23:49:12
LS2023-03-28 07:37:28
F2023-03-28 08:19:45
alax2023-04-03 12:49:21
Bear2023-04-26 08:57:44
Ziyan2023-04-27 12:36:58
bloop2023-05-09 17:51:42
leo2023-05-11 13:04:06
Mary P0ppins2023-05-11 15:52:29
hi2023-05-12 14:52:18
test4562023-05-18 18:30:09
test1232023-05-18 18:32:45
pinekiller2023-05-18 19:10:55
test12023-05-22 11:45:13
2023-05-26 10:10:33
aafan2023-05-26 10:10:52
Pansy2023-05-26 10:11:34
pågjøvikharvidetbra2023-05-26 10:14:47
ehsun2023-10-11 14:44:53
omstaendlig2024-03-03 08:21:28

6. You're not my type.

After breaking the extension check in the previous challenge, the developers decided to skip the extension check and instead go for a solid and secure type check.

The code below is used to block any non-image upload:

$type = $_FILES['file']['type'];
if( !($type == "image/jpeg" || $type == "image/png" || $type == "image/gif") ) {
  die("Wrong file type, only images allowed!");
}

Can you upload a file with the .php file extension, that also executes the function win();? I.e. the file should contain <?php win(); ?>.

Your username:
File:

Solvers:

Username Date
Augmenta2021-09-06 03:40:30
Q2022-03-22 12:19:16
φ2022-03-23 19:49:33
Peptski2022-03-25 17:21:59
Intel® HD Graphics2022-03-28 16:26:20
nightmorning2022-03-30 11:27:25
PotatoKingTheVII2022-06-17 15:00:01
TITANPOINTE2022-08-19 07:27:57
Assured2022-08-23 20:47:03
har-ee2022-11-02 21:52:24
Grybto2022-12-06 10:05:51
ludde2023-03-20 12:57:56
AT2023-03-24 16:10:48
AN2023-03-24 16:27:49
shyhungalpha2023-03-27 19:12:39
LS2023-03-28 08:50:33
F2023-03-28 09:11:26
alax2023-04-04 14:24:25
Corgo2023-04-24 00:01:58
Bear2023-04-26 10:00:19
Ziyan2023-04-27 19:24:05
2023-05-09 15:47:46
WOOP2023-05-09 15:51:18
g2023-05-09 15:52:25
bloop2023-05-09 19:22:35
mr hackerman2023-05-09 19:23:27
leo2023-05-11 14:51:02
Mary P0ppins2023-05-11 16:44:46
wizzer2023-05-18 14:09:15
firefox2023-05-18 17:31:57
pinekiller2023-05-18 20:22:54
test1112222023-05-22 07:18:10
2023-05-22 13:10:14
ehsun2023-10-11 15:04:59

7. Whatever, it's encrypted anyway.

Apparently it's very hard to implement a nice check for blocking uploading PHP files. Some developers probably thought "upload whatever you want, we'll just encrypt it so it can't execute.".

I can add that when I found this there were already a few malicious PHP files that had been uploaded. But none of them had "bypassed" the encryption. So maybe it does work?

The code below is used to encrypt the upload:

function xor_encrypt($data, $k0, $k1, $k2, $k3, $k4, $k5) {
  $key = Array($k0, $k1, $k2, $k3, $k4, $k5);
  $len = strlen($data);
  for ($i = 0; $i < $len; $i++) {
    $data[$i] = chr(ord($data[$i]) ^ ord($xor_key[$i % 6]));
  }
  return $data;
}

save($filename, xor_encrypt($data, 'b', 'e', 'n', 'e', 'r', 'i'));

Can you upload a file with the .php file extension, that also executes the function win();? I.e. the file should contain <?php win(); ?>.

Your username:
File:

Solvers:

Username Date
grocid2021-08-28 18:37:23
Goldfish2021-12-22 18:20:05
Peptski2022-03-22 09:47:05
Q2022-03-22 13:32:18
φ2022-03-23 20:00:56
Intel® HD Graphics2022-03-28 16:35:28
nightmorning2022-03-30 13:25:12
Koftan2022-03-31 21:43:19
Assured2022-05-10 00:44:46
PotatoKingTheVII2022-06-17 15:07:48
TITANPOINTE2022-08-24 11:29:58
har-ee2022-11-02 22:23:21
2022-12-01 16:42:35
ludde2023-03-20 13:00:58
Corgo2023-04-24 00:03:26
pinekiller2023-05-18 20:41:44
Simon Goater2023-05-24 15:12:05
ehsun2023-10-12 07:28:52

8. Crack the shell.

I was recently attacked by hackers from TAPESH-TEAM. In their attack, they tried to upload a web shell to my server. To ensure that other hackers won't be able to use the shell they add a password. However, I think they skipped their security courses. Can you gain access to the shell? Open TAPESH-SHELL-v1.0.php Source: TAPESH-SHELL-v1.0.php.txt. Original available on GitHub (no spoilers).

Once you get access the flag will be of the form realsec{...}.

Below is the core logic for the password check. You do not know the value of "SECRET".

if(!isset($_COOKIE['TapeshPassword']))
{
    setcookie('TapeshPassword',md5("SECRET"),time() + (86400 * 30));
}
@$password = $_POST['password'];
if(@$_COOKIE['TapeshPassword'] == md5($password))
{
    setcookie('Tapeshlog','true',time() + (86400 * 30));
}
else
{
    if(!isset($_COOKIE['Tapeshlog']) 
       || $_COOKIE['Tapeshlog']=="false" 
       || !isset($_COOKIE['Tapeshlog']))
    {
        $Eform='<form method="post"><input type="password" name="password"></form>';
        echo "...You don't have permission to access...";
        exit;
    }
}
FLAG
Your username:
Flag:

Solvers:

Username Date
φ2022-03-25 10:08:48
Q2022-03-25 16:34:13
Peptski2022-03-25 17:29:32
nightmorning2022-03-30 13:38:23
Koftan2022-03-31 20:53:04
Intel® HD Graphics2022-04-01 19:22:58
Assured2022-05-10 14:34:34
PotatoKingTheVII2022-06-17 21:32:42
TITANPOINTE2022-08-23 21:42:13
har-ee2022-11-02 22:00:00
Grybto2022-12-01 12:41:39
ludde2023-03-20 13:05:18
AT2023-03-24 17:39:51
LS2023-03-28 08:11:14
F2023-03-28 08:43:23
alax2023-04-04 14:47:52
Corgo2023-04-24 00:07:49
Bear2023-04-26 10:24:48
g2023-05-09 14:43:17
bloop2023-05-09 19:31:51
mr hackerman2023-05-09 19:33:17
leo2023-05-11 15:52:12
Mary P0ppins2023-05-11 17:23:11
:D2023-05-18 18:03:37
pinekiller2023-05-18 20:44:57
mad2023-05-19 10:58:13
2023-05-19 13:50:19
x2023-05-22 14:17:55
ehsun2023-10-11 15:18:16

9. It takes two to tango.

In this challenge, you have access to your files homework.txt and food.txt. However, the target has a juicy file called flag.txt. Sadly, we don't have permission to read it. Can you still get it?

I first saw (and reported) this problem on a Swedish course management system that is very popular with Universities. It wasn't until I saw it again on a very different type of website that I decided it could be worth sharing.

Once you get access the flag will be of the form realsec{...}.

My files:
homework.txt
food.txt

Target's files:
flag.txt

Your username:
Flag:

Solvers:

Username Date
TITANPOINTE2022-08-20 21:25:24
Assured2022-08-22 23:14:50
PotatoKingTheVII2022-08-29 20:03:31
codewarrior02022-08-29 20:16:10
corgo2022-08-29 21:00:47
φ2022-10-31 04:21:06
har-ee2022-11-02 21:47:06
Grybto2022-12-01 14:08:20
Q2023-03-20 08:39:23
Peptski2023-03-20 11:53:54
ludde2023-03-20 13:07:27
AT2023-03-24 17:56:34
LS2023-03-28 08:15:27
F2023-03-28 09:36:36
alax2023-04-04 15:32:35
Bear2023-04-26 10:24:48
g2023-05-09 14:48:57
mr hackerman2023-05-09 18:22:16
leo2023-05-11 15:24:59
Mary P0ppins2023-05-11 18:16:26
hi2023-05-12 19:18:08
bloop2023-05-18 09:17:18
542023-05-18 18:18:02
pinekiller2023-05-18 20:55:49
mad2023-05-19 10:58:47
2023-05-23 08:39:28
loop2023-05-23 08:39:50
xxx2023-05-23 09:26:19
ehsun2023-10-11 15:27:26

10. Crypto Catastrophe.

Just as in Bitcoin, the goal here is to find an input that gives a smaller (lexicographically) hash than the target hash. Simple right? Hopefully, you can do this without trying about 1620 (or 1024) hashes.

So far my challenges have focused on other people's mistakes but I'm not perfect either. Therefore, for this number 10 special, I thought it would be fitting to showcase a mistake I made. Can you hack me?

$hex_hash_str = md5($_POST['guess']);
$target_hash = "00000000000000000000dec0dec0ffee";

if($hex_hash_str < $target_hash) {
  win();
}

Your username:
Input:

Solvers:

Username Date
TITANPOINTE2022-08-22 18:02:34
Assured2022-08-23 22:11:46
PotatoKingTheVII2022-08-29 19:27:54
codewarrior02022-08-29 20:35:17
har-ee2022-11-02 22:15:31
Q2023-03-20 09:13:52
2023-03-20 12:33:58
Peptski2023-03-20 12:48:57
ludde2023-03-20 13:08:47
Corgo2023-04-23 23:39:32

11. The R in firewall stands for Regex.

The server in this case had a vulnerability that allowed attackers to upload PHP files in the image directory. However, thanks to a quite popular Web Application Firewall (WAF), the attackers were not able to execute their code.

Access images/shell.php to read the flag.

Once you get access the flag will be of the form realsec{...}.

Below is the regex check used by the WAF. It is rewritten by me in PHP but the regex is exactly the same.

$uri = $_SERVER["REQUEST_URI"];

$r = "(\/(images|img(s)?|pictures|upload(s)?)\/[^\.]{0,108}\.(pht|phtml|php\d?$))";

if(preg_match($r, $uri)) {
  echo "Blocked by L33TWAF!";
} else {
  echo $flag;
}

I have reported this and while they appreciate the feedback they argue this rule should only be used for logging.

The real website where this was found had many malicious PHP files in the image folder but seemed fine otherwise. Perhaps this WAF rule is not too bad!

Your username:
Flag:

Solvers:

Username Date
Peptski2023-03-20 12:07:52
Q2023-03-20 12:09:45
ludde2023-03-20 13:12:03
PotatoKingTheVII2023-03-27 14:16:56
TITANPOINTE2023-03-30 14:50:19
har-ee2023-03-31 00:09:02
Corgo2023-04-23 23:23:14
ehsun2023-10-12 06:39:43

12. If it's wide like an image and high like an image, it's an ...?

As we have seen, file types and extensions can not be trusted! "No problem" said these developers, as they have the function getimagesize, which will give us the width and height of an image.

$filename = basename($_FILES['file']['name']);
$size == getimagesize($_FILES['file']['tmp_name']);
$width = $size[0];
$height = $size[1];

if ($width && $width <= 1337 && $height && $height <= 1337) {
  // upload to the file
}

Can you upload a file with the .php file extension, that also executes the function win();? I.e. the file should contain <?php win(); ?>.

Your username:
File:

Solvers:

Username Date
Q2023-03-20 09:43:09
ludde2023-03-20 13:16:45
Peptski2023-03-20 16:16:18
PotatoKingTheVII2023-03-27 13:59:45
har-ee2023-03-31 00:15:53
2023-04-23 23:27:44
Corgo2023-04-23 23:28:23
ehsun2023-10-12 07:15:25