伺服器入侵之找出隱藏字元的原理

2022-09-05 18:00:17

一、文章起源

我在文章 一次伺服器被入侵的處理過程分享 中間有提及到我通過 cat/more 等命令檢視檔案,以及通過crontab -l 命令檢視,一些定時任務和檔案內容被隱藏了。

二、細講問題

檔案內容為何顯示不了,究竟是什麼東西在作怪,讓我們一探究竟。

為了讓大家也可以一起了解下, 檔案我放到 阿里雲oss 上, 有興趣的可以下載下來看下。 隱藏檔案內容範例

2.1、 檔案在不同環境下的不同編譯器開啟的顯示情況

Linux 通過 cat/more 開啟

Linux 通過 vim 開啟

Notepad++ 顯示所有字元模式開啟

開始自己網上查了一下, 沒有查到原因。憑藉著做多年運維解決問題的經驗,已經對問題的那種直覺感, 總覺得應該是 ;^M(我裝到了)。

三、 藉助場外資源

為了一探究竟,特意叫一個朋友拉我進了一個安全群,在群裡詢問下安全大佬。

3.1、雙向文字(Bidirectional tex)

具體可以看下這個文章 https://tttang.com/archive/1339/

在安全群裡問了一下,有位大佬提了一句 雙向文字方向。 我都沒有聽說過這個東西。 雙向文字到底是啥,科普下:雙向文字就是一個字串裡面,包含了兩種文字,既包含從左到右的文字,又包含從右向左的文字。

範例1:

文字中是

RLI a b c PDI

而顯示實際是

c b a

範例2:

文字中是

RLI LRI a b c PDI LRI d e f PDI PDI

而實際上顯示的是

d e f a b c

範例3:

程式碼顯示內容為

實際執行的內容是

細看,這好像跟我們的場景還是不一樣。 這個只是調換文字的順序。飯可以亂吃,路不能亂走啊,得走正道。 繼續瞅瞅。

3.2、 看看^M

有另外一個大佬說出來了一個問題, 所有的問題原因是 跟^M 有關。

如何在vim 中打出 ^M, ctrl + v +m 可以輸出 ^M

大佬說到 ^M 是回車換行符。 cat 帶有^M的一行字串時,螢幕上會把 ^M 之後的內容在同一行換行後輸出,這樣就會覆蓋掉^M之前的內容,導致你看到的這個內容效果。

我產生了疑問, ^M 是回車換行符, 這個有依據嗎? 在下面有解釋。

先不管這個,我們先測試一波。

範例一

[root@vm-12-12-centos tmp]# cat test 
1bc
[root@vm-12-12-centos tmp]# cat -A test 
abc^M1$

範例二

[root@vm-12-12-centos tmp]# cat -A test 
abc^M123$
[root@vm-12-12-centos tmp]# cat  test 
123

範例三

[root@vm-12-12-centos tmp]# cat -A test 
abc^M1    45$
[root@vm-12-12-centos tmp]# cat test 
1    45

通過上面的範例,我們可以知道,^M 後面的內容會覆蓋 ^M 前面的內容, 入侵者就是利用這個原理,在 ^M 後面打了很多個空格覆蓋掉前面實際定時任務的內容。 哎,這入侵者真是個人才。

3.3、真相大白

前面我產生了疑問, ^M 是回車換行符, 這個有依據嗎? 我該怎麼去檢視這cat 命令把這個 ^M 給識別成啥了?

在憋了幾天之後,終於想到了一個大器(strace)。 有點相見恨晚的感覺。

[root@vm-12-12-centos tmp]# strace cat test 
execve("/bin/cat", ["cat", "test"], 0x7ffe1d047e08 /* 19 vars */) = 0
brk(NULL)                               = 0x1c0d000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f14da021000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=22981, ...}) = 0
mmap(NULL, 22981, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f14da01b000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156592, ...}) = 0
mmap(NULL, 3985920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f14d9a33000
mprotect(0x7f14d9bf7000, 2093056, PROT_NONE) = 0
mmap(0x7f14d9df6000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f14d9df6000
mmap(0x7f14d9dfc000, 16896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f14d9dfc000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f14da01a000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f14da018000
arch_prctl(ARCH_SET_FS, 0x7f14da018740) = 0
access("/etc/sysconfig/strcasecmp-nonascii", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/sysconfig/strcasecmp-nonascii", F_OK) = -1 ENOENT (No such file or directory)
mprotect(0x7f14d9df6000, 16384, PROT_READ) = 0
mprotect(0x60b000, 4096, PROT_READ)     = 0
mprotect(0x7f14da022000, 4096, PROT_READ) = 0
munmap(0x7f14da01b000, 22981)           = 0
brk(NULL)                               = 0x1c0d000
brk(0x1c2e000)                          = 0x1c2e000
brk(NULL)                               = 0x1c2e000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106176928, ...}) = 0
mmap(NULL, 106176928, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f14d34f0000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
open("test", O_RDONLY)                  = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "abc\r1    45\n", 65536)        = 12
1    45, "abc\r1    45\n", 12abc
)          = 12
read(3, "", 65536)                      = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

我們注意看, read 這裡的內容。

read(3, "abc\r1    45\n", 65536)        = 12

我們發現 ^Mcat 命令執行過程中,轉換為 \r , 那麼 \r 又是什麼? 接下來需要去翻閱 新華字典了。

資料來源 https://man7.org/linux/man-pages/man7/ascii.7.html

\r : 回車符(carriage ret), 對應ASCII值13(縮寫:CR)。 它的含義是什麼: 回車 (控制字元)。

https://zh.wikipedia.org/zh-cn/回車符

那我們前面通過 notepad++ 開啟的線上的 CR 是不是就對上了。

最終我們可以得出一個初步結論, 也就是:

  • 在 Linux Vim 中列印出來的 ^M cat 等一些命令會轉換為 \r\r 也就是回車 在Linuxunix 中 表示它將遊標返回到行首。 \r之後的內容也就會覆蓋前面的內容。

四、注意

  1. 以後伺服器如果入侵了,建議我們使用 cat -A 命令來檢視。這樣一些隱藏字元就可以看到了。

參考文章:https://man7.org/linux/man-pages/man7/ascii.7.html