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-03-25 - Wow! Great job Q on solving all the original seven 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:


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

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') {

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:


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

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--) {
  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:


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

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:


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

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:


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

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:


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

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:


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

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".

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


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