golang 的一些安全問題

寒假主要是先出完了之前心心念念的typescript 題目,之后主要是抽點(diǎn)時(shí)間學(xué)golang.同學(xué)推薦的倉庫講的還是很不錯(cuò)的

https://github.com/unknwon/the-way-to-go_ZH_CN

從簡(jiǎn)單的開發(fā)與學(xué)習(xí)過程中可以感覺到golang的確非常安全,很多問題由于作為強(qiáng)類型的靜態(tài)語言,從編譯過程就能防止不安全的操作。因此大部分漏洞產(chǎn)生還是歸結(jié)于依賴庫(通常已經(jīng)發(fā)展為CVE或者issue)以及開發(fā)者自身的操作漏洞。這里就作為小結(jié),簡(jiǎn)單談一談

interger overflow/truncation

golang 存在非常經(jīng)典的整數(shù)反轉(zhuǎn)/溢出問題

對(duì)無符號(hào)數(shù)的反轉(zhuǎn)

func main() {
    var a uint32 = 2147483648
    var b uint32 = 2147483648
    var sum  =  a + b
    fmt.Println(reflect.TypeOf(sum))
    fmt.Printf("Sum is : %d",sum)
}
//uint32
//Sum is : 0

象要直接聲明一個(gè)大小已經(jīng)溢出的數(shù)自然不會(huì)通過編譯,因此出現(xiàn)反轉(zhuǎn)的話,主要是在變量的相加這樣的計(jì)算才會(huì)會(huì)導(dǎo)致標(biāo)志CF位反轉(zhuǎn)

有符號(hào)數(shù)的溢出

var a int8 = 127
var b int8 = 1
var sum int8 =  a + b
fmt.Println(reflect.TypeOf(sum))
fmt.Printf("Sum is : %d",sum)
//int8
//-128

具體的值可以看到math包中的常量定義。


image

在類型轉(zhuǎn)換中,也會(huì)出現(xiàn)較大整型向較小整型轉(zhuǎn)換的截?cái)鄦栴}

var a int16 = 256
var b  = int8(a)
fmt.Println(b)
// 0 
// high-order byte is truncated

我們可以看到整型安全的檢查也出現(xiàn)在了gosec中,一個(gè)比較經(jīng)典的例子就是:kubectl命令行中出現(xiàn)了一個(gè)strconv.Atoi導(dǎo)致的截?cái)鄦栴}。當(dāng)我們傳入port參數(shù)的對(duì)應(yīng)字符串后,容器啟動(dòng)的端口這一參數(shù)會(huì)將經(jīng)Atoi處理后的字符串進(jìn)行int32的類型轉(zhuǎn)換。由于64位系統(tǒng)的int是int64類型。轉(zhuǎn)int32的話會(huì)出現(xiàn)明顯截?cái)?br> 可以簡(jiǎn)化成以下代碼:

v , _ := strconv.Atoi("4294967377")
s  := int32(v)
fmt.Println(s)
// 81

這樣就有可能導(dǎo)致81端口的服務(wù)啟動(dòng),或者被停止。所以使用ParseInt ,ParseUInt會(huì)比較好。或者對(duì)端口號(hào)進(jìn)行限制。

CTF中出現(xiàn)整數(shù)溢出的scenario 一般都是 xx shop之類的。通過重復(fù)的加數(shù)進(jìn)行溢出然后鑒權(quán)。

pseudo-rand

golang 的math/rand 包中,我們可以看到隨機(jī)數(shù)生成的函數(shù)形式

image

跟進(jìn)一下函數(shù)與結(jié)構(gòu)體

var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)})

......

func NewSource(seed int64) Source {
    var rng rngSource
    rng.Seed(seed)
    return &rng
}

可以看到,這些隨機(jī)數(shù)函數(shù)的seed默認(rèn)為1.也就是說如果不使用rand.Seed()確認(rèn)種子的話,生成的只是所謂的偽隨機(jī)數(shù)。

這種問題可能會(huì)出現(xiàn)在開發(fā)者直接用rand生成的字符串作為密鑰的場(chǎng)景,比如gin的cookie的key.從而導(dǎo)致本地復(fù)現(xiàn),直接鑒權(quán)。

go net/http

net/http < 1.11 CRLF

低版本的http庫會(huì)導(dǎo)致CRLF.比如http.NewRequest()。貌似是原本沒有問題,但是在一次升級(jí)中疏忽了導(dǎo)致重新出現(xiàn)

image

現(xiàn)在會(huì)看到存在限制,我們無法傳入\r\n的字符

WMCTF中的gogogo出現(xiàn)過這一漏洞,利用自然是通過CRLF發(fā)送POST請(qǐng)求進(jìn)行繞過與上傳。

weird stuff

在之前的justCTF中,出現(xiàn)了一道go題。題目原本的漏洞是由出題人發(fā)現(xiàn)的一個(gè)issuehttps://github.com/golang/go/issues/40940 加上其對(duì)fileServer一些代碼的魔改組合的。

簡(jiǎn)單陳述下的話,題目提供了一個(gè)go http起的FileServer

http.Handle("/", http.FileServer(http.Dir("/tmp")))

flag就在其提供文件服務(wù)的文件夾下,但是,出題人加上了web服務(wù)的flag路由,從而使得我們沒法通過直接訪問/flag來獲取文件。而是得到/flag路由的回顯。

出題人的意圖是利用其挖掘到的這個(gè)漏洞,造成錯(cuò)誤的文件讀取范圍。通過訪問其他文件越界讀到flag.(http的Fileserver在我們?cè)L問時(shí),會(huì)先根據(jù)我們?cè)L問的url進(jìn)行一系列處理,杜絕路徑穿越的url,之后進(jìn)行文件讀取返回給用戶)

但是比較有意思的時(shí),比賽中出現(xiàn)了一個(gè)非預(yù)期讀flag的方式
curl --path-as-is -X CONNECT http://gofs.web.jctf.pro/../flag

簡(jiǎn)單說就是用CONNECT請(qǐng)求+路徑穿越的url讀取到了文件。我們看看源碼是怎么處理的

image

如果是CONNECT方式請(qǐng)求,就不會(huì)處理url中的特殊字符。導(dǎo)致直接讀取flag.其他的請(qǐng)求方法都會(huì)在cleanPath中被處理url

算是一個(gè)很有意思的問題。不過golang1.16似乎已經(jīng)處理了。但是包括我在內(nèi)的大部分應(yīng)該還是使用的go1.15及以下版本,所以還是值得注意的

feature of slice

之前在做buu上的roarCTF時(shí)遇到一道go題,其中一個(gè)利用點(diǎn)來自Confidence CTF,其中用到了golang slice的一個(gè)feature

a := make([]uint64, 0)
a = append(a, 1)
a = append(a, 2)
a = append(a, 3)
b := append(a, 4)
c := append(a, 5)

fmt.Println(a)
fmt.Println(b)
fmt.Println(c)

//result:
//[1 2 3]
//[1 2 3 5]
//[1 2 3 5]

按照直覺來說,b這里應(yīng)該是[1,2,3,4],但實(shí)際上卻是[1,2,3,5]
這就與golang的slice也就是切片的結(jié)構(gòu)相關(guān)了

type slice struct {
    array unsafe.Pointer // ptr
    len   int
    cap   int
}
image

slice結(jié)構(gòu)中的cap是按2的倍數(shù)擴(kuò)容的。所以說當(dāng)我們append(3)時(shí)會(huì)發(fā)生第一次擴(kuò)容,此時(shí)len為3,cap為2*2=4.
執(zhí)行b := append(a, 4)時(shí),我們的4會(huì)被放在指針ptr的第四個(gè)位置。然后返回ptr len=4 cap=4給b。不過這并沒有改變a的結(jié)構(gòu)(slice只是指向內(nèi)存的指針)之后進(jìn)行c := append(a, 5)時(shí),由于a沒變,新元素只會(huì)覆蓋之前b那放上的4.

那一道題目就是利用這點(diǎn),可以通過先beg三次構(gòu)造出len=3,cap=4的切片,通過題目中一個(gè)append超大隨機(jī)數(shù)的機(jī)會(huì),在下一次beg時(shí)達(dá)成元素覆蓋。

這個(gè)可以算是slice的feature,還是很有意思的。

reDOS

原本是以為golang不存在reDOS的問題的。當(dāng)初閱讀lmt-swallow的文章時(shí)也看到他提到golang有l(wèi)inear time matching。 不過后來發(fā)現(xiàn)還是有途徑的。

當(dāng)然,reDOS主要還是在js里影響比較大,因?yàn)槠涫录?duì)列阻塞對(duì)于web服務(wù)的影響是巨大的。相比之下其他的DOS影響可能就較小。用nginx做個(gè)處理基本就沒有影響。不過,如果是以Blind regex injection為目的考慮reDOS的話,就另當(dāng)別論了。

回到golang上。golang使用的正則引擎是re2。但是用的是無回溯機(jī)制(DFA)。
所以之前的payload如^(?=(PAYLOAD))((.*)*)*salt$就無效了

但是,通過其他方法制造延時(shí)仍然是可行的。比如re2對(duì)于{n}這樣的正則,n存在有上限1000,且對(duì)于DFA狀態(tài)數(shù)沒有限制.我們可以通過不斷重復(fù)(.?){1000}來制造足夠的DFA狀態(tài)進(jìn)行一定程度上的延時(shí)

reDOS其實(shí)一直是一個(gè)很有意思的點(diǎn),除了喜聞樂見的nodejs,前端中xss也可以導(dǎo)致這個(gè)問題,從而xsleak ,ruby中也有類似問題。當(dāng)然,如果是從reDOS引申到正則盲注的話,sql注入中倒也有類似的情況。總之就是利用差異性進(jìn)行數(shù)據(jù)提取。可惜因?yàn)樘菀讐沫h(huán)境了,所以很難出道題到比賽中 :(

SSTI

最后一個(gè)就簡(jiǎn)單提一下ssti.要說golang這種靜態(tài)編譯還會(huì)出ssti還是挺難的( 除非源碼中用sprintf 或者說拼接來構(gòu)建template的參數(shù)

var tmpl = fmt.Sprintf(`<h2>No search results for %s</h2>`, xxx)

golang的template跟很多模板引擎的語法差不多,比如{{}}指定可解析的對(duì)象。{{.xxx}}表示一個(gè)對(duì)象的xxx屬性。.就是當(dāng)前作用域的當(dāng)前對(duì)象,基本就跟handlebars里的this差不多。

假如我們傳入的參數(shù)是可解析的,就有可能泄露template在執(zhí)行過程中引入的對(duì)象內(nèi)容(執(zhí)行的本質(zhì)就是合并替換)

簡(jiǎn)單的demo可以參考這里https://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html

如果傳入的struct屬性含有指針的話(非常常見,因?yàn)橛弥羔樋梢怨?jié)省很很多空間),我們ssti的回顯就只是一個(gè)地址。需要我們手動(dòng)去訪問屬性才會(huì)解引

Summary && Reference

簡(jiǎn)單的梳理下自己見過的golang安全問題(CTF-based web go)。其實(shí)像之前dasCTF中也有g(shù)olang的題目,用gob序列化存內(nèi)容到cookie,不過利用點(diǎn)與go的關(guān)系為0,所以不談。

自己golang的開發(fā)還有很多值得去學(xué)的,這門語言確實(shí)有其優(yōu)越之處以及一些可能存在并被挖掘的安全問題

https://github.com/tsg-ut/tsgctf2020
https://annevi.cn/2020/08/14/wmctf2020-gogogo-writeup/
https://github.com/golang/go/issues/30794
https://github.com/golang/go/issues/40940
https://github.com/mwarzynski/confidence2019_teaser_lottery

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容