Inhaltsverzeichnis
Runde 1: Sich ständig ändernde Intranetadressen
Runde 2: URL-Sprung
Runde 3: DNS-Rebinding
Heim Backend-Entwicklung Golang Angriff und Verteidigung der SSRF in Go

Angriff und Verteidigung der SSRF in Go

Jul 24, 2023 pm 02:05 PM
go ssrf

Was ist SSRF?

SSRF wird im Englischen als Server Side Request Forgery geschrieben, was als serverseitige Anforderungsfälschung übersetzt wird. Wenn es dem Angreifer nicht gelingt, Serverberechtigungen zu erhalten, nutzt er die Serverschwachstelle aus, um eine konstruierte Anfrage als Server an das Intranet zu senden, in dem sich der Server befindet. Was die Zugriffskontrolle von Intranet-Ressourcen betrifft, muss sich jeder darüber im Klaren sein.

Angriff und Verteidigung der SSRF in Go

Wenn die obige Aussage nicht leicht zu verstehen ist, dann wird Lao Xu nur ein praktisches Beispiel geben. Viele Schreibplattformen unterstützen mittlerweile das Hochladen von Bildern über URLs. Wenn der Server URLs nicht streng überprüft, können böswillige Angreifer möglicherweise auf Intranetressourcen zugreifen.

„Ein tausend Meilen langer Deich wird in einem Ameisennest einstürzen.“ Wir Programmierer sollten keine Lücken ignorieren, die Risiken verursachen können, und solche Lücken werden wahrscheinlich zu einem Sprungbrett für die Leistung anderer. Um nicht zum Sprungbrett zu werden, wird Lao Xu mit allen Lesern einen Blick auf die Offensiv- und Defensivrunden der SSRF werfen.

Runde 1: Sich ständig ändernde Intranetadressen

Warum das Wort „sich ständig ändern“ verwenden? Lao Xu wird vorerst nicht antworten, aber die Leser lesen bitte geduldig weiter. Im Folgenden verwendet Lao Xu 182.61.200.7(www.baidu.com的一个IP地址)这个IP和各位读者一起复习一下IPv4的不同表示方式。

Angriff und Verteidigung der SSRF in Go

注意⚠️:点分混合制中,以点分割地每一部分均可以写作不同的进制(仅限于十、八和十六进制)。

上面仅是IPv4的不同表现方式,IPv6的地址也有三种不同表示方式。而这三种表现方式又可以有不同的写法。下面以IPv6中的回环地址0:0:0:0:0:0:0:1 als Beispiel.

Angriff und Verteidigung der SSRF in Go

Hinweis⚠️: Die führende 0 jedes Formulars. Ebenso wie die 0-Bit-komprimierte Darstellung und die eingebettete IPv4-Adressdarstellung kann auch eine IPv6-Adresse in unterschiedlichen Darstellungen geschrieben werden.

Nachdem er so viel geredet hat, kann Lao

Interne IP, glauben Sie, dass sie hier ist? Natürlich nicht! Ich weiß nicht, ob irgendein Leser davon gehört hat, xip.io这个域名。xip可以帮你做自定义的DNS解析,并且可以解析到任意IP地址(包括内网)。

Angriff und Verteidigung der SSRF in Go

我们通过xip提供的域名解析,还可以将内网IP通过域名的方式进行访问。

关于内网IP的访问到这儿仍将继续!搞过Basic验证的应该都知道,可以通过http://user:passwd@hostname/ auf Ressourcen zuzugreifen. Wenn der Angreifer die Schreibweise ändert, kann er möglicherweise einige der weniger strengen Logiken umgehen, wie unten gezeigt.

Angriff und Verteidigung der SSRF in Go

In Bezug auf die Intranetadresse hat Lao

Lao Es wird niemanden geben, der reguläre Ausdrücke verwenden kann, um die obige Filterung durchzuführen. Wenn ja, hinterlassen Sie mir bitte eine Nachricht, damit ich daraus lernen kann.

Wir haben die verschiedenen Intranetadressen grundsätzlich verstanden, daher stellt sich nun die Frage, wie wir diese in eine IP umwandeln können, die wir beurteilen können. Zusammenfassend lassen sich die oben genannten Intranetadressen in drei Kategorien einteilen: 1. Es handelt sich um eine IP-Adresse selbst, deren Ausdruck jedoch nicht einheitlich ist. 2. Ein Domänenname, der auf die Intranet-IP verweist. 3. Eine Adresse, die eine Basisüberprüfung enthält Informationen und die Intranet-IP. Anhand dieser drei Arten von Merkmalen können Sie die Intranetadresse identifizieren und den Zugriff verweigern, indem Sie die folgenden Schritte ausführen, bevor Sie eine Anfrage initiieren.

  1. Parsen Sie den Hostnamen in der Adresse.

  2. Initiieren Sie die DNS-Auflösung und beziehen Sie die IP.

  3. Stellen Sie fest, ob es sich bei der IP um eine Intranetadresse handelt.

Bei der Beurteilung der Intranetadresse in den obigen Schritten ignorieren Sie bitte nicht die IPv6-Loopback-Adresse und die eindeutige lokale IPv6-Adresse. Das Folgende ist die von Lao Xu verwendete Logik, um zu bestimmen, ob es sich bei der IP um eine Intranet-IP handelt.

// IsLocalIP 判断是否是内网ip
func IsLocalIP(ip net.IP) bool {
if ip == nil {
return false
	}
// 判断是否是回环地址, ipv4时是127.0.0.1;ipv6时是::1
if ip.IsLoopback() {
return true
	}
// 判断ipv4是否是内网
if ip4 := ip.To4(); ip4 != nil {
return ip4[0] == 10 || // 10.0.0.0/8
			(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
			(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
	}
// 判断ipv6是否是内网
if ip16 := ip.To16(); ip16 != nil {
// 参考 https://tools.ietf.org/html/rfc4193#section-3
// 参考 https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses
// 判断ipv6唯一本地地址
return 0xfd == ip16[0]
	}
// 不是ip直接返回false
return false
}
Nach dem Login kopieren

Das Bild unten zeigt das Ergebnis der Befolgung der oben genannten Schritte, um festzustellen, ob es sich bei der Anfrage um eine Intranet-Anfrage handelt.

Angriff und Verteidigung der SSRF in Go

Zusammenfassung: URLs gibt es in verschiedenen Formen, und Sie können die DNS-Auflösung verwenden, um die standardisierte IP zu erhalten und so festzustellen, ob es sich um eine Intranetressource handelt.

Runde 2: URL-Sprung

Wenn böswillige Angreifer nur über unterschiedliche Arten des Schreibens von IP angreifen, können wir uns natürlich zurücklehnen und entspannen, aber dieser Kampf zwischen Speer und Schild hat gerade erst begonnen.

Lassen Sie uns die Verteidigungsstrategie in Runde 1 überprüfen. Die Feststellung, ob es sich bei der Anfrage um eine Intranetressource handelt, erfolgt, bevor die Anfrage offiziell initiiert wird. Wenn der Angreifer während des Anfrageprozesses über einen URL-Sprung auf die Intranetressource zugreift, kann er Runde 1 vollständig umgehen Strategie. Der spezifische Angriffsprozess ist wie folgt.

Angriff und Verteidigung der SSRF in Go

Wie in der Abbildung gezeigt, kann ein Angreifer durch URL-Sprung an Intranetressourcen gelangen. Bevor Lao Xu und seine Leser sich mit der Abwehr von URL-Jump-Angriffen befassen, werden sie zunächst den HTTP-Umleitungsstatuscode 3xx überprüfen.

Laut Wikipedia gibt es 9 3xx-Weiterleitungscodes im Bereich von 300 bis 308. Lao ;font-size : 11.9px;padding: 0.2em 0.4em;background-color: rgba(27, 31, 35, 0.05);border-radius: 3px;">http.ClientDie ausgegebene Anfrage unterstützt nur die folgenden Werte: Orientierungscode. http.Client发出的请求仅支持如下几个重定向码。

301

301 : Die angeforderte Ressource wurde dauerhaft an einen neuen Speicherort verschoben; die Antwort kann zwischengespeichert werden; die Umleitungsanforderung muss eine GET-Anfrage sein.

302:要求客户端执行临时重定向;只有在Cache-Control或Expires中进行指定的情况下,这个响应才是可缓存的;重定向请求一定是GET请求。

303:当POST(或PUT / DELETE)请求的响应在另一个URI能被找到时可用此code,这个code存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源;303响应禁止被缓存;重定向请求一定是GET请求。

307:临时重定向;不可更改请求方法,如果原请求是POST,则重定向请求也是POST。

308: Permanente Umleitung; die Anfragemethode kann nicht geändert werden. Wenn die ursprüngliche Anfrage POST ist, ist die umgeleitete Anfrage auch POST.

Das war's mit der Überprüfung des 3xx-Statuscodes. Lassen Sie uns die Diskussion über die Offensiv- und Defensivrunden der SSRF fortsetzen. Da das Springen von URLs auf der Serverseite Risiken mit sich bringen kann, können wir solche Risiken vollständig vermeiden, solange wir das Springen von URLs deaktivieren. Dies ist jedoch nicht möglich, obwohl dieser Ansatz Risiken vermeidet, es ist jedoch sehr wahrscheinlich, dass normale Anfragen versehentlich beschädigt werden. Wie kann man solche Angriffe verhindern?

看过老许“Go中的HTTP请求之——HTTP1.1请求流程分析”这篇文章的读者应该知道,对于重定向有业务需求时,可以自定义http.Client的CheckRedirect。下面我们先看一下CheckRedirect的定义。

CheckRedirect func(req *Request, via []*Request) error
Nach dem Login kopieren

这里特别说明一下,req是即将发出的请求且请求中包含前一次请求的响应,via是已经发出的请求。在知晓这些条件后,防御URL跳转攻击就变得十分容易了。

  1. 根据前一次请求的响应直接拒绝307308的跳转(此类跳转可以是POST请求,风险极高)。

  2. 解析出请求的IP,并判断是否是内网IP。

根据上述步骤,可如下定义http.Client

client := &http.Client{
	CheckRedirect: func(req *http.Request, via []*http.Request) error {
// 跳转超过10次,也拒绝继续跳转
if len(via) >= 10 {
return fmt.Errorf("redirect too much")
		}
		statusCode := req.Response.StatusCode
if statusCode == 307 || statusCode == 308 {
// 拒绝跳转访问
return fmt.Errorf("unsupport redirect method")
		}
// 判断ip
		ips, err := net.LookupIP(req.URL.Host)
if err != nil {
return err
		}
for _, ip := range ips {
if IsLocalIP(ip) {
return fmt.Errorf("have local ip")
			}
			fmt.Printf("%s -> %s is localip?: %v\n", req.URL, ip.String(), IsLocalIP(ip))
		}
return nil
	},
}
Nach dem Login kopieren

如上自定义CheckRedirect可以防范URL跳转攻击,但此方式会进行多次DNS解析,效率不佳。后文会结合其他攻击方式介绍更加有效率的防御措施。

Zusammenfassung: URL-Jump-Angriffe können durch Anpassung verhindert werdenhttp.ClientCheckRedirect.

Runde 3: DNS-Rebinding

Wie wir alle wissen, müssen Sie zum Initiieren einer HTTP-Anfrage zunächst den DNS-Dienst anfordern, um die dem Domänennamen entsprechende IP-Adresse zu erhalten. Verfügt der Angreifer über einen kontrollierbaren DNS-Dienst, kann er die bisherige Abwehrstrategie durch DNS-Rebinding und Angriff umgehen.

Der spezifische Prozess ist im Bild unten dargestellt.

Angriff und Verteidigung der SSRF in Go

Bei der Überprüfung, ob die Ressource legal ist, führte der Server die erste DNS-Auflösung durch und erhielt eine Nicht-Intranet-IP mit einer TTL von 0. Beurteilen Sie die aufgelöste IP und stellen Sie fest, dass Nicht-Intranet-IP für nachfolgende Anforderungen verwendet werden kann. Da der DNS-Server des Angreifers die TTL auf 0 setzt, muss die DNS-Auflösung erneut durchgeführt werden, wenn die Anfrage offiziell initiiert wird. Zu diesem Zeitpunkt gibt der DNS-Server die Intranetadresse zurück. Da er in die Ressourcenanforderungsphase eingetreten ist und keine Abwehrmaßnahmen vorhanden sind, kann der Angreifer an die Intranetressourcen gelangen.

Als zusätzliche Erwähnung: Lao .

在发起请求的过程中有DNS解析才让攻击者有机可乘。如果我们能对该过程进行控制,就可以避免DNS重绑定的风险。对HTTP请求控制可以通过自定义http.Transport来实现,而自定义http.Transport也有两个方案。

方案一

dialer := &net.Dialer{}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
	host, port, err := net.SplitHostPort(addr)
// 解析host和 端口
if err != nil {
return nil, err
	}
// dns解析域名
	ips, err := net.LookupIP(host)
if err != nil {
return nil, err
	}
// 对所有的ip串行发起请求
for _, ip := range ips {
		fmt.Printf("%v -> %v is localip?: %v\n", addr, ip.String(), IsLocalIP(ip))
if IsLocalIP(ip) {
continue
		}
// 非内网IP可继续访问
// 拼接地址
		addr := net.JoinHostPort(ip.String(), port)
// 此时的addr仅包含IP和端口信息
		con, err := dialer.DialContext(ctx, network, addr)
if err == nil {
return con, nil
		}
		fmt.Println(err)
	}

return nil, fmt.Errorf("connect failed")
}
// 使用此client请求,可避免DNS重绑定风险
client := &http.Client{
	Transport: transport,
}
Nach dem Login kopieren

transport.DialContext的作用是创建未加密的TCP连接,我们通过自定义此函数可规避DNS重绑定风险。另外特别说明一下,如果传递给dialer.DialContext方法的地址是常规IP格式则可使用net包中的parseIPZone函数直接解析成功,否则会继续发起DNS解析请求。

方案二

dialer := &net.Dialer{}
dialer.Control = func(network, address string, c syscall.RawConn) error {
// address 已经是ip:port的格式
	host, _, err := net.SplitHostPort(address)
if err != nil {
return err
	}
	fmt.Printf("%v is localip?: %v\n", address, IsLocalIP(net.ParseIP(host)))
return nil
}
transport := http.DefaultTransport.(*http.Transport).Clone()
// 使用官方库的实现创建TCP连接
transport.DialContext = dialer.DialContext
// 使用此client请求,可避免DNS重绑定风险
client := &http.Client{
	Transport: transport,
}
Nach dem Login kopieren

dialer.Control在创建网络连接之后实际拨号之前调用,且仅在go版本大于等于1.11时可用,其具体调用位置在sock_posix.go中的(*netFD).dial方法里。

Angriff und Verteidigung der SSRF in Go

Die beiden oben genannten Verteidigungslösungen können nicht nur DNS-Rebinding-Angriffe, sondern auch andere Angriffsmethoden verhindern. Tatsächlich empfahl Lao Xu sogar die zweite Option, die eine einmalige Lösung darstellt!

Zusammenfassung:

  1. Ein Angreifer kann einen DNS-Rebinding-Angriff über seinen eigenen DNS-Dienst durchführen.

  2. DNS-Rebinding-Angriffe können durch Anpassung verhindert werdenhttp.Transport.

Das obige ist der detaillierte Inhalt vonAngriff und Verteidigung der SSRF in Go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Vertiefendes Verständnis des Golang-Funktionslebenszyklus und des Variablenumfangs Vertiefendes Verständnis des Golang-Funktionslebenszyklus und des Variablenumfangs Apr 19, 2024 am 11:42 AM

In Go umfasst der Funktionslebenszyklus Definition, Laden, Verknüpfen, Initialisieren, Aufrufen und Zurückgeben; der Variablenbereich ist in Funktionsebene und Blockebene unterteilt. Variablen innerhalb einer Funktion sind intern sichtbar, während Variablen innerhalb eines Blocks nur innerhalb des Blocks sichtbar sind .

Wie sende ich Go WebSocket-Nachrichten? Wie sende ich Go WebSocket-Nachrichten? Jun 03, 2024 pm 04:53 PM

In Go können WebSocket-Nachrichten mit dem Paket gorilla/websocket gesendet werden. Konkrete Schritte: Stellen Sie eine WebSocket-Verbindung her. Senden Sie eine Textnachricht: Rufen Sie WriteMessage(websocket.TextMessage,[]byte("message")) auf. Senden Sie eine binäre Nachricht: Rufen Sie WriteMessage(websocket.BinaryMessage,[]byte{1,2,3}) auf.

Wie kann ich Zeitstempel mithilfe regulärer Ausdrücke in Go abgleichen? Wie kann ich Zeitstempel mithilfe regulärer Ausdrücke in Go abgleichen? Jun 02, 2024 am 09:00 AM

In Go können Sie reguläre Ausdrücke verwenden, um Zeitstempel abzugleichen: Kompilieren Sie eine Zeichenfolge mit regulären Ausdrücken, z. B. die, die zum Abgleich von ISO8601-Zeitstempeln verwendet wird: ^\d{4}-\d{2}-\d{2}T \d{ 2}:\d{2}:\d{2}(\.\d+)?(Z|[+-][0-9]{2}:[0-9]{2})$ . Verwenden Sie die Funktion regexp.MatchString, um zu überprüfen, ob eine Zeichenfolge mit einem regulären Ausdruck übereinstimmt.

Der Unterschied zwischen Golang und Go-Sprache Der Unterschied zwischen Golang und Go-Sprache May 31, 2024 pm 08:10 PM

Go und die Go-Sprache sind unterschiedliche Einheiten mit unterschiedlichen Eigenschaften. Go (auch bekannt als Golang) ist bekannt für seine Parallelität, schnelle Kompilierungsgeschwindigkeit, Speicherverwaltung und plattformübergreifende Vorteile. Zu den Nachteilen der Go-Sprache gehören ein weniger umfangreiches Ökosystem als andere Sprachen, eine strengere Syntax und das Fehlen dynamischer Typisierung.

Wie vermeidet man Speicherlecks bei der technischen Leistungsoptimierung von Golang? Wie vermeidet man Speicherlecks bei der technischen Leistungsoptimierung von Golang? Jun 04, 2024 pm 12:27 PM

Speicherlecks können dazu führen, dass der Speicher des Go-Programms kontinuierlich zunimmt, indem: Ressourcen geschlossen werden, die nicht mehr verwendet werden, wie z. B. Dateien, Netzwerkverbindungen und Datenbankverbindungen. Verwenden Sie schwache Referenzen, um Speicherlecks zu verhindern, und zielen Sie auf Objekte für die Garbage Collection ab, wenn sie nicht mehr stark referenziert sind. Bei Verwendung von Go-Coroutine wird der Speicher des Coroutine-Stapels beim Beenden automatisch freigegeben, um Speicherverluste zu vermeiden.

Wie kann ich die Golang-Funktionsdokumentation in der IDE anzeigen? Wie kann ich die Golang-Funktionsdokumentation in der IDE anzeigen? Apr 18, 2024 pm 03:06 PM

Go-Funktionsdokumentation mit der IDE anzeigen: Bewegen Sie den Cursor über den Funktionsnamen. Drücken Sie den Hotkey (GoLand: Strg+Q; VSCode: Nach der Installation von GoExtensionPack F1 und wählen Sie „Go:ShowDocumentation“).

Eine Anleitung zum Unit-Testen gleichzeitiger Go-Funktionen Eine Anleitung zum Unit-Testen gleichzeitiger Go-Funktionen May 03, 2024 am 10:54 AM

Das Testen gleichzeitiger Funktionen in Einheiten ist von entscheidender Bedeutung, da dies dazu beiträgt, ihr korrektes Verhalten in einer gleichzeitigen Umgebung sicherzustellen. Beim Testen gleichzeitiger Funktionen müssen grundlegende Prinzipien wie gegenseitiger Ausschluss, Synchronisation und Isolation berücksichtigt werden. Gleichzeitige Funktionen können Unit-Tests unterzogen werden, indem Rennbedingungen simuliert, getestet und Ergebnisse überprüft werden.

Wie verwende ich den Fehler-Wrapper von Golang? Wie verwende ich den Fehler-Wrapper von Golang? Jun 03, 2024 pm 04:08 PM

In Golang können Sie mit Fehler-Wrappern neue Fehler erstellen, indem Sie Kontextinformationen an den ursprünglichen Fehler anhängen. Dies kann verwendet werden, um die von verschiedenen Bibliotheken oder Komponenten ausgelösten Fehlertypen zu vereinheitlichen und so das Debuggen und die Fehlerbehandlung zu vereinfachen. Die Schritte lauten wie folgt: Verwenden Sie die Funktion „errors.Wrap“, um die ursprünglichen Fehler in neue Fehler umzuwandeln. Der neue Fehler enthält Kontextinformationen zum ursprünglichen Fehler. Verwenden Sie fmt.Printf, um umschlossene Fehler auszugeben und so mehr Kontext und Umsetzbarkeit bereitzustellen. Wenn Sie verschiedene Fehlertypen behandeln, verwenden Sie die Funktion „errors.Wrap“, um die Fehlertypen zu vereinheitlichen.

See all articles