드림핵 command-injection-1 Write Up
문제 설명
flag.py에 플래그가 존재하니 command 명령어를 통해 flag.py 파일을 읽으면 문제를 해결할 수 있다.
문제 풀이
ping 페이지에 접속하면 host ip를 입력하는 페이지가 보여진다.
일단 8.8.8.8로 ping을 전송해보겠다.
그림 2는 8.8.8.8로 ping을 보낸 결과이다.
3번 ping을 보내는 것을 보면 ping -c 3 {사용자 입력} 이런식으로 코드가 이루어져 있다고 예상이 된다.
일단 소스 코드를 한번 살펴 보겠다.
@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')
request.form.get으로 host의 값을 불러온 후 cmd 변수에다 집어 넣는다. 해당 과정에서 입력값 검증이 이루어지지 않고 있기 때문에 command injection 취약점이 발생한다.
output 변수를 보면 subprocess.check_output으로 command 명령어를 실행하고 있다.
/bin/sh을 실행하는데 -c 옵션으로 인자를 받는다 인자로 cmd 변수를 실행한다.
command injection에서 메타문자를 이용하여 다중 명령어를 실행할 수 있다.
;(세미콜론)을 사용하면 앞에 명령어의 결과와 상관없이 차례대로 명령어를 수행하기 때문에 세미콜론을 이용해서 문제를 해결하였다.
ping -c 3 "사용자 입력값" 에서 사용자 입력값을 변경한다.
payload : ping -c 3 "8.8.8.8"; ls ; echo ""
위의 payload를 이용하면 ping 명령어를 3번 수행하고 ls 명령어를 수행하고 echo 명령어를 수행한다. 여기에서 ls명령어로 현재 디렉터리에 어떤 파일들이 있는지 확인한다.
요청한 형식과 일치 하지 않다고 해당 명령어가 수행되지 않고 있다.
페이지 소스보기를 통해 왜 오류가 발생하는지 살펴본다.
페이지 소스보기를 한 결과 해당 input박스에는 pattern이 적용되어 있다. 숫자와 대소문자와 점으로 이루어져있어야하며 5~20글자 사이여야한다.
해당 패턴으로 메타문자 입력을 못하도록 보안조치가 되어있어서 command injection을 방어한다고 생각할 수 있지만, 이것은 클라이언트 쪽에서 필터링이 이루어져 있기 때문에 우회가 가능하다.
f12에서 pattern을 삭제하고 해당 payload를 입력하거나 burpsuite를 이용하여 우회가 가능하다.
필자는 burpsuite를 이용하였다.
host 매개 변수에 payload를 입력한 후 전송을하면 ls 명령어가 수행되는것을 볼 수 있다.
그 결과 flag.py 파일이 있음을 확인하였다.
해당 payload에서 ls를 cat flag.py로 변경하여 flag.py 파일을 읽도록 하였고 그 결과가 반환되었다.
Flag는 DH{pingpingppppppppping!!}이다.