해당 문제는 드림핵의 커맨드 인젝션-1번 문제입니다. command injection-1
Command injection 말 그대로 명령어 삽입 공격인 것 같은데 이 문제에서 해당 기법을 처음 접해봤습니다.
SQL Injection 처럼 명령 구문을 삽입하는 공격이리라,, 하고 풀이해봤습니다.
사이트는 /index, /ping 두 개의 URL이 존재합니다.
문제를 풀기 전 함께 제공된 app.py를 살펴보겠습니다.
#!/usr/bin/env python3
# app.py
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)
cmd = f'ping -c 3 "{host}"'
서버는 flask로 짜였고 index페이지와 /ping 두 URL 중
/ping의 ping() 메서드에 cmd = f'ping -c 3 "{host}"' 항목이 눈에 띕니다.
{host} 부분에 command injection 취약점이 발생할 명령을 삽입해야 하는 듯합니다.
8.8.8.8로 ping 테스트를 수행합니다.
실제 ping 명령과 동일한 기능을 수행하고 이 결과를 출력하는 것을 알 수 있습니다.
ping -c 3 {host}
[입력값];ls
command injection을 발생시키는 방법은 주로 "메타 문자"를 통해 발생합니다.
위 서버 코드를 살펴보면 ping 기능을 입력받는 부분이 존재하고
{host}라는 입력값을 받는 걸 알 수 있는데 메타 문자로 변조된 공격코드를 {host} 부분에 삽입하면
command injection의 취약점이 발생한다는 걸 알 수 있습니다.
저는 수많은 메타 문자 중 세미콜론(;)을 통한 변조 코드를 작성하여 삽입하겠습니다.
ls;cat test1.txt
위 세미콜론(;)을 통해 앞의 명령어의 에러 유무와 관계없이 뒷 명령어를 실행하는 즉
구분 자이지만 무조건 실행된다는 것을 알 수 있습니다.
구분자를 통해 간단히 취약점이 발생할 거라 생각했지만 안되니 당황해서
다양한 변조 코드를 짜서 삽입해보았으나 전부 안됐습니다.
입력값에 대한 검증이 따로 있는지 살펴보지는 않고 바로 해서 그런가 싶어서
동봉된 다른 코드들을 또 뒤져봤습니다.
/*ping.html*/
{% extends "base.html" %}
{% block title %}ping{% endblock %}
{% block head %}
{{ super() }}
{% endblock %}
{% block content %}
<h1>Let's ping your host</h1><br/>
<form method="POST">
<div class="row">
<div class="col-md-6 form-group">
<label for="Host">Host</label>
<input type="text" class="form-control" id="Host" placeholder="8.8.8.8" name="host" pattern="[A-Za-z0-9.]{5,20}" required>
</div>
</div>
<button type="submit" class="btn btn-default">Ping!</button>
</form>
{% endblock %}
templates/ping.html 파일입니다.
코드를 살펴보시면
pattern="[A-Za-z0-9.]{5,20}"
pattern에서 정규표현식을 통해 입력값 host에 대한 검증이 이루어지고 있었습니다.
그런데 사실 저는 이게 온전히 검증 코드인지 아닌지 살펴볼 코드에 대한 적절한 지식이 없는지라.. 확답은 할 수가 없습니다만
정규표현식이 괜히 사용되어 있지는 않을 것 같고 검증이건 아니건 제가 넣은 입력값에 대한 무언가의 조치가 취해져 있다고
생각할 뿐입니다.
그럼 일단 정규표현식에 대해 살짝 알아는 봐야겠습니다 궁금하니까요,,
위에 있던 정규표현식 값을 그대로 넣어 확인해봤습니다.
뜻을 대강 해석해보자면
알파벳 대문자, 소문자 전부 허용,
숫자는 0-9까지 그리고 점 문자 허용,
마지막의 글자 수의 길이 5-20까지 허용
대략 이렇습니다.
그렇다면 결론은 이겁니다. 위 정규표현식 조건에 해당하지 않는 세미콜론(:)과 같은
메타 문자는 사용이 안된다는 뜻이며 즉 필터링이 된다는 의미이죠
그러니 검증이 되는지 안되는지 확실히 몰랐지만 정규표현식 조건에 메타 문자가 들어있지 않다는 것만으로
원리에 대해 자세히 알고 있는 것은 아니지만 검증이 수행되고 있다는 점을 확인했습니다.
HTTP Request에서 입력값에 대한 검증 우회의 방법에는 여러 가지가 있습니다만
저는 버프 스위트 프로그램을 사용하는 걸 선호합니다.
웹 프락시를 설정해준 후 버프 스위트의 인터셉트를 통해 request를 잡아줍니다.
intercept를 통해 전송되고 있는 request를 가로챘습니다.
정상 전송되는 Host값을 변조(공격코드 삽입)하여 서버에 전달하겠습니다.
아까 맨 처음에 떠올렸던 메타 문자인 세미콜론을 삽입한 인젝션 코드를 작성해줍니다.
/ping에서 ls 명령어가 정상적으로 수행되었음을 알 수 있습니다.
그렇게 서버에 존재하는 파일의 리스트가 출력되었고
우리가 찾아야 할 flag.py파일이 존재한다는 걸 알게 되었고
해당 플래그 파일을 cat/more을 통해 읽어 들이면 끝입니다.
8.8.8.8";"cat flag.py
간단한 문제라 생각해서 바로 해봤으나 안되더라고요
아무래도 cat과 flag.py 사이의 공백 또한 정규표현식을 통한 필터링에 걸리나 싶어서
이 공백또한 메타 문자를 이용해 처리를 해줘야 하나 싶어서 "로 감싸 봤습니다.
8.8.8.8";"cat" "flag.py
이것저것 해보다가 때려 맞춘 거긴 합니다만
아직도 왜 저 공백까지 메타 문자로 처리를 해주어야 하는지에 대한 이해가 잘 되지는 않고 있습니다.
아까 살펴본 정규표현식 조건상에서는. 만 허용되는 것 같은데 어째서 "로 감싸는 건 가능한 걸까.... 싶은 그런
하지만 때려 맞춘 보람은 있었나 봅니다.
cat 명령어는 온전히 제 기능을 수행해 flag값을 출력해냈습니다.
웹 해킹이 해킹 분야 중 가장 쉽게 접하는 분야라지만 그럼에도 쉽지는 않은듯합니다.
특히 이해가 되지 않는 부분에 대한 설명이나 답을 찾아내는 것 또한 어렵더라고요,,
아무튼 공부 열심히 합시다!
댓글