아래는 서버 코드의 소스코드이다.
ping함수를 유심히 보자
#!/usr/bin/env python3
import subprocess
from flask import Flask, request, render_template, redirect
from flag import FLAG
APP = Flask(__name__)
@APP.route('/')
def index():
return render_template('index.html')
@APP.route('/ping', methods=['GET', 'POST'])
def ping():
if request.method == 'POST':
host = request.form.get('host')
cmd = f'ping -c 3 "{host}"'
try:
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
return render_template('ping_result.html', data=output.decode('utf-8'))
except subprocess.TimeoutExpired:
return render_template('ping_result.html', data='Timeout !')
except subprocess.CalledProcessError:
return render_template('ping_result.html', data=f'an error occurred while executing the command. -> {cmd}')
return render_template('ping.html')
if __name__ == '__main__':
APP.run(host='0.0.0.0', port=8000)
host에 입력값을 받는 것을 볼 수 있는데 ping -c 3 "ip주소" 이런식으로 받는 것이 정상적인 작동이다.
하지만 우리는 Command Injection이라는 취약점을 사용해서 flag.py의 내용을 확인할 것이다.
이 역시 post 방식으로 받은 값을 필터링 없이 사용해서 생기는 Injection의 전형적인 취약점이다.
이전에 공부했던 것처럼 cat은 파일의 내용을 보여주는 명령어이고 연속적인 명령어를 실행하려면 ;, &&과 같은 메타문자를 사용해야한다.
그럼 ping -c 3 "ip주소" 이곳에 어떤 입력값을 넣어야할까
8.8.8.8 ; cat "flag.py" 이라는 커맨드를 주입하면 된다.
하지만 끈 따옴표가 자체적으로 있기 때문에 이 역시도 전에 했던 Injection처럼 따옴표를 따로 처리 해주면 된다.
그에 따라서 8.8.8.8" ; cat "flag.py 이러한 커맨드를 입력하게 되면
ping -c 3 "8.8.8.8" ; cat "flag.py"
이런 커맨드가 된다.
그럼 직접 입력해보자
이 문제는 사실 입력폼에 설정되어 있는 패턴을 지워서 특수문자도 입력될 수 있게 하면 브라우저에서 직접 풀 수 있는 문제이다.
하지만 나는 버프슈트를 이용해서 프록시 서버에서 패킷을 가로채고 조작하여 서버에 요청해보겠다.
먼저 패킷을 잡기 위해 8.8.8.8 만 대입해서 서버에 요청해본다.
그리고 프록시 서버에서 그 패킷을 가로챈다.
이후 맨 밑 페이로드를 8.8.8.8" ; cat "flag.py로 바꾸어 주고 다시 forward로 서버에 요청하면
이렇게 flag.py 를 볼 수 있고 그 안에 flag를 얻을 수 있다.