久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Netty服務被攻擊實例分析

194次閱讀
沒有評論

共計 3901 個字符,預計需要花費 10 分鐘才能閱讀完成。

本篇內容介紹了“Netty 服務被攻擊實例分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

故事前奏

Netty 服務是公司比較邊緣的服務,只有一臺設備在使用,而且代碼是之前技術 Leader(已離職)寫的,加上一直趕工期,所以就沒抽出時間去徹底解決這事。

當初被攻擊沒排查代碼,看到遭到瘋狂請求、CPU 跑滿、日志打滿,還以為是遭遇 DDoS 攻擊了。

臨時采取了幾個措施:

分離服務器,確保該服務遭到攻擊時不會拖垮其他服務;

換了一個 IP 和端口;

針對攻擊的 IP 添加黑名單;

在代碼層,發現非法請求強制關閉連接;

添加日志信息,追溯攻擊報文和源頭;

對攻擊服務的 IP(上海阿里云的)進行舉報;

但沒多久,黑客又找上門來了,十天半月來一次攻擊,好像知道服務 IP 和后臺代碼似的,陰魂不散。

這不,今天被逮到了,而且之前添加了日志打印,也拿到了攻擊的報文內容,復現了攻擊操作。

//  攻擊者第一次嘗試的報文  8000002872FE1D130000000000000002000186A00001977C0000000000000000000000000000000000000000 //  攻擊者第二次嘗試的報文  8000002872FE1D130000000000000002000186A00001977C00000000000000000000000000000000

上述報文,第一次的報文觸發了攻擊,第二次的報文沒有影響(與正常業務報文格式無異)。

下面就帶大家分析分析攻擊的邏輯和代碼中存在的漏洞。

知識儲備

要了解攻擊的原理,我們需要有一定的 Netty 技術知識。關于 Netty 如何實現客戶端和服務器端的代碼這里就不展開了,可以看一下實現實例:https://github.com/secbr/netty-all/tree/main/netty-decoder

我們重點了解一下自定義解碼器和 io.netty.buffer.ByteBuf。其中自定義解碼器用于對報文進行解析,而報文內容通過 ByteBuf 進行緩存傳輸。

上面的攻擊報文格式表明,黑客已經“猜到”我們是基于 16 進制 Btye 格式進行內容傳輸的(黑客竟然也知道)。

自定義解碼器

要自定義解碼器,繼承 MessageToMessageDecoder 類并實現 decode 方法即可,下面展示一下示例代碼:

public class MyDecoder extends MessageToMessageDecoder ByteBuf  { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List Object  out) { } }

其中解析報文的邏輯便是在 decode 方法內進行處理。其中 ByteBuf  in 就是接收傳入報文的容器,而 List out 用于輸出解析之后的結果。

下面來看一下有 bug 的代碼(已經過脫敏處理):

protected void decode(ChannelHandlerContext ctx, ByteBuf in, List Object  out) { int readableBytes = in.readableBytes(); while (readableBytes   3) { in.skipBytes(2); int pkgLength = in.readUnsignedShort(); in.readerIndex(in.readerIndex() - 4); if (in.readableBytes()   pkgLength) { return; } out.add(in.readBytes(pkgLength)); readableBytes = in.readableBytes(); } }

上面的代碼在跑正常業務時是沒問題的,但當被攻擊時,就進入了死循環。因此,導致雖然在業務處理時添加了關閉連接的操作也是無效的。

在分析上面代碼之前,我們還得先詳細分析一下 ByteBuf 的原理。

ByteBuf 的原理

ByteBuf 中會維護兩個索引:一個索引 (readIndex) 用于讀取,一個索引 (writeIndex) 用于寫入。

當從 ByteBuf 讀取時,readIndex 會被遞增已經被讀取的字節數,當向 ByteBuf 中寫入數據時,writeIndex 也會被遞增。

netty-ByteBuf

上面圖以攻擊的報文為例進行展示,攻擊者用了 44 個字節的報文進行攻擊。由于使用的是 16 進制,所以兩個字符占用 1 個字節。

readIndex 和 writeIndex 的起始位置的索引位置都為 0,當執行 ByteBuf 中的 readXXX 或 writeXXX 方法時,會推進對應的索引。當執行 setXXX 或 getXXX 方法的操作時則不會。

了解了 ByteBuf 的基本處理原理之后,我們就來對照攻擊者的報文和源代碼來進行攻擊過程的還原。

攻擊還原

下面直接通過源代碼一步步的分析,主要涉及 ByteBuf 類的方法。有效攻擊的報文為上面提到的第一個報文。

//  攻擊者第一次嘗試的報文  8000002872FE1D130000000000000002000186A00001977C0000000000000000000000000000000000000000

下面來看代碼:

int readableBytes = in.readableBytes();

這行代碼通過 readableBytes 方法獲取到當前 ByteBuf 中可以讀到的字節數,上述攻擊報文 88 個字符,所以這里得到 44 個字節。

當 readableBytes 大于 3 時便進行具體的解析處理:

in.skipBytes(2);

很明顯,通過 skipBytes 方法跳過了兩個字節。

netty-ByteBuf

int pkgLength = in.readUnsignedShort();

通過 readUnsignedShort 方法,獲得了 2 個字節的內容,這兩個字節對應的十六進制值為“0028”,對應十進制為“40”。這兩個字節在報文中的含義是 (部分或整個) 報文的長度。

報文的長度往往有兩種算法:第一,長度代表整個報文的長度(業務中使用的含義); 第二,長度代表除前 4 個字節之后的報文長度(攻擊者使用的含義)。

其實,正是因為這個長度含義的定義,導致正常業務可以執行,而攻擊報文會進入死循環。

下面繼續分享代碼:

in.readerIndex(in.readerIndex() - 4);

經上面的 skipBytes 和 readUnsignedShort 的調用,ByteBuf 的讀索引已經跑到了第 4 個字節上了。所以這里 in.readerIndex()返回的值為 4,而 in.readerIndex(4-4)的作用就是將讀索引重置為 0,也就是從頭開始讀。

if (in.readableBytes()   pkgLength) { return; }

這個判斷是在讀索引移動到 0 之后,看看報文的可讀字節數是否小于報文內容中指定的字節數。很顯然,in.readableBytes()對應的值為 44 個字節,而 pkgLength 為 40 個字節,不會進行 return。

out.add(in.readBytes(pkgLength));

讀取 40 個字節,進行輸出。還剩下 4 個字節的內容,readIndex 指向第 40 個字節的位置。

readableBytes = in.readableBytes();

由于 readIndex 已經指向第 40 個字節,所以此時可讀字節數為 4。

然后,進入第二輪循環。此時,神奇的情況就出現了。我們可以看到攻擊的后 4 個字節的報文值全為 0。

in.skipBytes(2); int pkgLength = in.readUnsignedShort();

因此跳過 2 個字節后,readIndex 為 42,pkgLength 獲取第 43 和 44 字節的值:0。

in.readerIndex(in.readerIndex() - 4);

上述代碼又將 readIndex 設置到第 40 個字節。

if (in.readableBytes()   pkgLength) { return; }

此時會發現 readableBytes 返回值為 4,但 pkgLength 已經變為 0 了,不會 return。

接下讀取內容時就出現狀況了:

out.add(in.readBytes(pkgLength)); //  這里還剩下 4 個字節  readableBytes = in.readableBytes();

上述 readBytes 讀取字節數為 0,而 readableBytes 始終為 4。此時,整個 while 循環進入了死循環,大量消耗 CPU 資源。

此時還沒完,最多只是把 CPU 跑到 100%,但是當不停的將空字符寫到接收數據的緩沖區域之后,緩沖區開始瘋狂調用處理業務的 Handler,進一步侵入到業務處理邏輯當中。

雖然業務邏輯層做了判斷,也進行了連接的關閉,但此時已經與連接無關,while 循環已經進入死循環,關掉連接也沒什么作用。同時,業務層有日志輸出,大量的日志輸出到磁盤當中,導致磁盤被刷滿。

最終導致服務器的 CPU 監控和磁盤監控報警。乍一看,還以為是又一次 DDoS 攻擊。

“Netty 服務被攻擊實例分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注丸趣 TV 網站,丸趣 TV 小編將為大家輸出更多高質量的實用文章!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-04發表,共計3901字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 庆元县| 福海县| 宜昌市| 乐安县| 宝坻区| 孟村| 靖州| 赤壁市| 奉贤区| 彭阳县| 黄大仙区| 洪湖市| 二连浩特市| 南木林县| 新沂市| 韶关市| 乌恰县| 红原县| 新源县| 乌审旗| 临沭县| 贵定县| 隆德县| 波密县| 永昌县| 甘泉县| 平安县| 漾濞| 定日县| 靖西县| 临桂县| 自治县| 依安县| 西乌珠穆沁旗| 亳州市| 东乡族自治县| 乃东县| 方正县| 榆中县| 廊坊市| 平武县|