개념정리
Command injection에 대해 알아보자.
○ Command Injection
인젝션은 악의적인 데이터를 프로그램에 입력하여 시스템 명령어, 코드, 데이터베이스 쿼리 등이 실행되게 하는 기법을 말한다. 이 중, 시스템 명령어를 실행하게 하는 취약점을 Command Injection이라고 부른다.
Command Injection은 사용자의 입력값을 제대로 검증하지 않는 경우에 발생한다.
특히, 리눅스에서는 여러 명령을 연속으로 실행할 수 있는 메타문자들을 제공하기 때문에 공격자는 Command Injection을 수행하여 본인이 원하는 명령을 실행시킬 수 있다.
○ 메타 문자 종류
- 명령어 치환
- ` `
- ` ` 안에 포함되는 명령어를 실행한 결과로 치환한다.
$()
$()
안에 들어있는 명령어를 실행한 결과로 치환한다.와 다르게 중복 사용이 가능하다.
- ` `
- 명령어 연속 실행
&&
- 한 줄에 여러 명령어를 사용하고 싶을 때 사용한다. 앞 명령어에서 에러가 발생하지 않아야 뒷 명령어를 실행한다.
||
- 1줄에 여러 명령어를 사용하고 싶을 때 사용한다. 앞 명령어에서 에러가 발생해야 뒷 명령어를 실행한다.
- 명령어 구분자
;
- 한 줄에 여러 명령어를 사용하고 싶을 때 사용한다. ";"은 단순히 명령어를 구분하기 위해 사용하며, 앞 명령어의 에러 유무와 관계 없이 뒷 명령어를 실행한다.
- 파이프
|
- 앞 명령어의 결과가 뒷 명령어의 입력으로 들어간다.
메타 문자들을 이용하면 공격자는 기존에 실행할 명령 외에 추가적인 명령을 삽입해서 원하는 명령을 실행할 수 있다.
문제분석
ping 테스트를 하는 페이지다.
IP 주소를 입력하는 과정에서 command injection을 수행하면 될 것 같다.
코드에서 어떤 방식으로 ping을 실행하는지 분석해보자.
@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')
ping -c 3 "{host}"
위 형태로 ping을 실행한다.
host를 입력받는 과정에서 검증이 존재하지 않는다. 따라서 ping 다음에 메타문자를 이용해서 추가적인 명령을 실행하도록 command injection을 수행할 수 있다.
리눅스에서는 ;
로 구분하면 여러개의 명령을 1줄에 실행할 수 있다.
즉, ping -c 3 127.0.0.1 ; ls
와 같이 명령을 실행하면 ping과 ls가 연속으로 실행된다.
문제풀이
ping -c 3 "127.0.0.1";cat flag.py;"ls
flag를 읽는 payload다.
주의할 점은 IP 주소가 ""
로 묶여 있기 때문에 "
를 사용해서 ip 주소 부분을 묶었다.
이어서 ;
를 사용해 cat flag.py
을 실행하도록 추가했다.
마지막 "
도 처리해야하므로 "ls
를 추가했다.
payload를 전송해보자.
요청한 형식과 일치시키라는 메세지가 등장한다.
<input>
태그에 pattern이라는 속성이 있고 정규표현식으로 value에 대한 패턴을 지정했다.
즉, 해당 패턴에 맞는 입력만 허용하는 상황이다.
.{5,100}
pattern 속성을 입력 제한이 없도록 위와 같은 정규표현식으로 바꾸고 payload를 전송해보자.
정규표현식에서 .
는 모든 문자/숫자를 의미하고 5~100 자리인 경우에 허용한다는 뜻이다.
개발자 도구로 pattern 속성을 새로운 정규표현식으로 바꿨다.
payload를 전송해보자.
대응 방안
- 입력값 검증
- 입력값을 검증하여, 악의적인 코드가 포함된 경우에는 요청을 거부한다.
- 입력값 이스케이핑
- 입력값을 이스케이핑하여, 시스템 명령어로 인식되지 않도록 한다.
- 사용자 입력으로 시스템 명령 실행 제한
- 사용자 입력값으로 시스템 명령을 실행하는 대신에 다른 방법을 사용한다.
- 실행 권한 제한
- 웹 애플리케이션이 실행하는 시스템 명령어의 실행 권한을 제한하여, 악의적인 명령어가 실행되지 않도록 한다.
- 웹 애플리케이션 업데이트
- 웹 애플리케이션의 보안 업데이트를 정기적으로 수행하여, 최신 보안 취약점에 대한 대응 조치를 취한다.
reference
[dreamhack] ServerSide: Command Injection