ISCC2023 web复现——小周的密码锁

由于本人打比赛的时候还是菜鸡一个,只做了个签到题,昨天刚好刷到了,就想着本地复现一下。

也是记录一下一个看似是web实则是crypto的题,难度我就不多说了,往下看就知道了。

访问地址

这是首页界面,我们现在随便输个值可以看到有两个参数

手动随便爆破或者BurpSuite跑一下可以发现当password2=5时会回显源码

本地copy下来,这里我只放关键代码

<?php
function MyHashCode($str)
{
    $h = 0;
    $len = strlen($str);
    for ($i = 0; $i < $len; $i++) {
        $hash = intval40(intval40(40 * $hash) + ord($str[$i]));
    }
    return abs($hash);
}

function intval40($code)
{
    $falg = $code >> 32;
    if ($falg == 1) {
        $code = ~($code - 1);
        return $code * -1;
    } else {
        return $code;
    }
}
function Checked($str){
    $p1 = '/ISCC/';
    if (preg_match($p1, $str)){
        return false;
    }
    return true;
}

function SecurityCheck($sha1,$sha2,$user){

    $p1 = '/^[a-z]+$/';
    $p2 = '/^[A-Z]+$/';

    if (preg_match($p1, $sha1) && preg_match($p2, $sha2)){
        $sha1 = strtoupper($sha1);
        $sha2 = strtolower($sha2);
        $user = strtoupper($user);
        $crypto = $sha1 ^ $sha2;
    }
    else{
        die("wrong");
    }

    return array($crypto, $user);
}
error_reporting(0);

$user = $_GET['username'];//user
$sha1 = $_GET['sha1'];//sha1
$sha2 = $_GET['‮⁦//sha2⁩⁦sha2'];
//‮⁦see me ⁩⁦can you

if (isset ($_GET['password'])) {
    if ($_GET['password2'] == 5){
        show_source(__FILE__);
    }
    else{
        //Try to encrypt
        if(isset($sha1) && isset($sha2) && isset($user)){
            [$crypto, $user] = SecurityCheck($sha1,$sha2,$user);
            if((substr(sha1($crypto),-6,6) === substr(sha1($user),-6,6)) && (substr(sha1($user),-6,6)) === 'a05c53'){//welcome to ISCC

                if((MyHashcode("ISCCNOTHARD") === MyHashcode($_GET['password']))&&Checked($_GET['password'])){
                    include("flag.php");
                    echo $flag;
                }else{
                    die("就快解开了!");
                }

            }
            else{
                die("真的想不起来密码了吗?");
            }
        }else{
            die("密钥错误!");
        }
    }
}

mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 1e4) + rand(1, 1e4));
?>

可以看到有5个if判断,绕过后即可获得flag

先看第一个,就是让我们GET传参一个password;第二个if就是当password2=5时回显源码,否则进入下一个if判断;第3个if是让我们GET传入sha1、"sha2"、username三个参数,至于这个sha2为什么要特别标注,这里我埋下个伏笔;第4个if是先通过 SecurityCheck($sha1,$sha2,$user) 这个函数将我们GET传入的sha1、"sha2"、username三个参数进行处理,最后返回的结果是将$sha1,$sha2的值进行按位异或得到 $crypto参数和我们GET传入的username的值赋给 $user,然后判断 $user 经过sha1加密后的后六位字符是否等于 'a05c53' ,$crypto 经过 sha1加密后的后六位字符是否等于$user 经过sha1加密后的后六位字符,也就是 'a05c53';最后一个if就是判断我们GET的password这个参数经过MyHashcode()函数后是否等于 MyHashcode("ISCCNOTHARD"),并且要经过Checked()函数的过滤,也就是不能含有 'ISCC'。

简单梳理过后我们可以发现,现在 $user 的值是最好确定的,可以写个py脚本爆破出来

import hashlib

for num in range(10000,9999999999):
    res = hashlib.sha1(str(num).encode()).hexdigest() 
    if res[-6:] == "a05c53":   
        print(str(num)) 
        break

爆破出来的结果为 14987637,也就是GET的username=14987637,现在$user的值确定了,根据上面我们对第3个if的分析,我们可以让$crypto的值也等于14987637,$crypto和$user值相等,他们的后六位自然相等。

需要注意这个函数,他会判断sha1是否是小写字母,sha2是否是大写写字母,是就会将sha1变成全大写字母,sha2变成全小写字母,这里只用注意传参值就行,因为异或我试过, a^P 和 A^p的结果是一样的。

function SecurityCheck($sha1,$sha2,$user){

    $p1 = '/^[a-z]+$/';
    $p2 = '/^[A-Z]+$/';

    if (preg_match($p1, $sha1) && preg_match($p2, $sha2)){
        $sha1 = strtoupper($sha1);
        $sha2 = strtolower($sha2);
        $user = strtoupper($user);
        $crypto = $sha1 ^ $sha2;
    }
    else{
        die("wrong");
    }

    return array($crypto, $user);
}

同样写个py脚本,我的脚本是爆出所以字母异或后的值是14987637这几个数字中的一个

for sha1 in 'abcdefghijklmnopqrstuvwsyz':

     for sha2 in 'ABCDEFGHIJKLMNOPQRSTUVWSYZ':

         sha = chr(ord(sha1) ^ ord(sha2));
         #print(sha)
         if sha in '14987637':
             print(sha1, sha2, sha)

将得到的结果拼接能得到14987637就行,我取的是sha1=aacaaaaa,sha2=PUZYVWRV

验证一下

那么接下来我就要说说我分析时留下的那个伏笔了,我把源码copy下来时用在线编译和记事本时

好像并没有什么问题,但是我copy到phpstorm时却发现了一个很有意思的东西

我们会发现sha2好像有地方乱码了,一开始我以为是显示问题,后来通过下面的文章我了解到了这是特殊的unicode编码

https://mp.weixin.qq.com/s/lo2AiEloACLtCn2Ncle33A

根据文章,我上面贴了一下浏览器页面,细心的朋友会发现,//sha2 的sha2的颜色是不是红一点呢?也就是说真正的 "sha2" 参数其实是 "$a ='sha2';//sha2" 进行urlencode编码后的值,因此"sha2" 参数真正的参数名是

%E2%80%AE%E2%81%A6%2F%2Fsha2%E2%81%A9%E2%81%A6sha2

因此需要GET传参 sha1=aacaaaaa&%E2%80%AE%E2%81%A6%2F%2Fsha2%E2%81%A9%E2%81%A6sha2=PUZYVWRV

说实话做到这里我真的惊叹了很久。。。之前我看了很多WP都没有提到这一点,每次看他们的payload的时候都不知道为什么sha2要这么传。。。

现在终于到了最后一个if的绕过,根据上面的分析,我们现在重点看这三个函数

function MyHashCode($str)
{
    $h = 0;
    $len = strlen($str);
    for ($i = 0; $i < $len; $i++) {
        $hash = intval40(intval40(40 * $hash) + ord($str[$i]));
    }
    return abs($hash);
}

function intval40($code)
{
    $falg = $code >> 32;
    if ($falg == 1) {
        $code = ~($code - 1);
        return $code * -1;
    } else {
        return $code;
    }
}
function Checked($str){
    $p1 = '/ISCC/';
    if (preg_match($p1, $str)){
        return false;
    }
    return true;
}

intval40它的作用是将一个64位整数转换为40位整数。具体实现是通过判断最高位是否为1来确定是否需要进行补码操作,然后将结果返回。其中,如果最高位为1,则需要进行补码操作,将其转换为相应的负数。最后,返回40位整数的值。这是我对代码做点修改方便看是否要取补码,yes是取,no是不取。

MyHashCode($str)这个函数就是将$hash(初始值为0)经过两次intval40后在加上传入字符串对应每个字符的ASCII码值,我们可以先跑一下代码方便理解

可以看到MyHashcode("ISCCNOTHARD") 的值是取其结果的最后一次,而 ISCC 这4个字符在intval40后都没取补码,现在我要让

MyHashcode("ISCCNOTHARD") === MyHashcode($_GET['password']),并且password里面不能有ISCC,那么我们是否可以用某些字符串代替ISCC,从而绕过呢?

也就是说我们现在给password的值在经过MyHashcode()后取最后一次结果的值要等于MyHashcode("ISCCNOTHARD") 的值是取其结果的最后一次结果的值,也就是787668828277355348。我们知道$hash的值一开始是0,经过MyHashcode的第一次循环后就是输入参数的第一个字符的ASCII码值,第二次循环后就是输入参数的第一个字符的ASCII码值乘以40后再加上第二个字符的ASCII码值,现在有个思路就是让其$hash的值在经过循环后可以等于73,因为I经过一次循环后的值是73,简单来说就是让

MyHashcode("ISCCNOTHARD") === MyHashcode("代替I的字符SCCNOTHARD"),简单点想

我们让ASCII码为1的字符为第一个字符,这样经过MyHashcode的第一次循环后$hash的值就是1,第二次循环后$hash的值就是1*40+第二个字符的ASCII码值(值是33,也就是 !号),这样$hash的值就等于73了。也就是 CET传参 password=%01!SCCNOTHARD   //ascii为1的字符是非可见                                                                                                               字符,经过url编码后为%01

这是我比较能理解的思路,网上别人也有对应的脚本,但我不明白为什么那样写,我把脚本贴下面了,我不理解的就是为什么一定要是 'IR'然后取拼接,我试过用'IA'、'IB'等都爆不出来,下面脚本最终爆出来是 IRkCNOTHARD。

<?php

function intval40($code)
{
    $falg = $code >> 32;
    if ($falg == 1) {
        $code = ~($code - 1);
        return $code * -1;
    } else {
        return $code;
    }
}

function MyHashCode($str)
{
    $h = 0;
    $len = strlen($str);
    for ($i = 0; $i < $len; $i++) {
        $hash = intval40(intval40(40 * $hash) + ord($str[$i]));
    }
    return abs($hash);
}


function Checked($str){
    $p1 = '/ISCC/';
    if (preg_match($p1, $str)){
        return false;
    }
    return true;
}

$a="abcdefghijklmnopqrstuvwxyz";
 
echo MyHashCode("ISCCNOTHARD");
for($i;$i<strlen($a);$i++) {
	echo "<br>";
 	echo MyHashCode("IS".$a[$i]."CNOTHARD");
 	if(MyHashCode("IR".$a[$i]."CNOTHARD")===MyHashCode("ISCCNOTHARD")){
 		echo "IR".$a[$i]."CNOTHARD";
	}
}

最终payload:

?password=%01!SCCNOTHARD&password2=1&username=14987637&sha1=aacaaaaa&%E2%80%AE%E2%81%A6%2F%2Fsha2%E2%81%A9%E2%81%A6sha2=PUZYVWRV

得到flag (太不容易了!!!!)

之后有时间我会继续复现 ISCC2023的 老狼老狼几点了和 where_is_you_love (我只复现POP链)

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值