리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 29 / 50 · Part 3. Linux 명령어 활용
실습 환경: Rocky Linux 9 (10.0.0.200) · 재현 실습 (아래 결과는 실제 출력)
이전 글: 28. Linux Shell Script 기초
지금까지 Part 3에서는 로그(과거 기록) 를 분석했다. 이번 글은 지금 살아 있는 시스템 을 분석한다. 28편의 스크립트가 "실패 후 성공한 계정(devuser)"을 찾아냈다면, 그 계정이 서버에 들어와서 무엇을 실행했고 어디와 통신하는지 를 밝히는 단계다.
핵심 주제는 "이름은 위장할 수 있지만, 커널이 관리하는 정보는 위장하기 어렵다" 는 것이다. 4편에서 커널 스레드를 흉내 낸 프로세스를 잠깐 봤는데, 이번에는 그런 프로세스 하나를 실제로 만들어서 ps, ss, lsof, /proc을 조합해 정체를 밝히는 전 과정을 보여준다.
아래 모든 출력은 실습 환경에서 실제로 재현해 얻은 결과 다. 악성코드가 아니라, Python으로 만든 "이름을
[kworker/0:2]로 위장하고 4444 포트를 여는 프로세스"다.
| 구분 | 항목 | 위장 방법 |
|---|---|---|
| 위장 쉬움 | 프로세스 이름 (comm, argv[0]) | exec -a '[kworker/0:2]' |
| 위장 어려움 | PPID (부모) | 커널이 관리 |
/proc/PID/exe (실제 실행 파일) | 커널이 관리 | |
| 열린 소켓·파일 | 커널이 관리 | |
| 실행 파일 해시 | 내용 자체 |
| 도구 | 확인 내용 | 관련 글 |
|---|---|---|
ps -ef --forest | 프로세스 트리, 부모-자식 | 4편 |
/proc/PID/{exe,cwd,cmdline,comm,status} | 실제 경로, 작업 디렉터리, 실행 인자 | 4·9편 |
ss -tanp | 프로세스별 네트워크 연결 | 1편, 44편 |
lsof -p PID | 프로세스가 연 파일·소켓 | 11편 |
sha256sum, file | 실행 파일 정체·해시 | 13편 |

분석은 "의심 → 정체 → 통신 → 자원 → 증적 → 대응" 순서로 좁혀 간다. 각 단계는 앞 단계에서 얻은 정보(PID)를 다음 도구에 넘기는 구조다.
# 이름을 커널 스레드처럼 위장하고 4444 포트를 여는 프로세스 (악성 기능 없음)
mkdir -p /tmp/.cache
cp "$(readlink -f "$(which python3)")" /tmp/.cache/kw
cd /tmp/.cache
( exec -a '[kworker/0:2]' /tmp/.cache/kw -c '
import socket,time
s=socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
s.bind(("0.0.0.0",4444)); s.listen(1); c,a=s.accept(); time.sleep(600)' & )
rm -f /tmp/.cache/kw # 공격자가 실행 후 파일 삭제 (11편)
# 1) 커널 스레드로 위장한 프로세스 찾기 (PPID가 2가 아닌데 이름이 [로 시작)
ps -eo pid,ppid,comm,args | awk '$3 ~ /^\[/ && $2!=2 && $2!=0'
P=$(pgrep -f 'kworker/0:2' | head -1) # 위에서 찾은 PID
# 2) 실제 정체
ls -l /proc/$P/exe /proc/$P/cwd
cat /proc/$P/comm
tr '\0' ' ' < /proc/$P/cmdline; echo
grep -E '^(PPid|Uid|State)' /proc/$P/status
# 3) 네트워크 연결
sudo ss -tanp | grep "pid=$P"
# 4) 열린 파일·소켓
sudo lsof -p $P
# 5) 증적 복구 (파일이 삭제됐어도 프로세스가 열고 있으면 복사 가능)
sudo mkdir -p /root/evidence
sudo cp /proc/$P/exe /root/evidence/kw.bin
sudo sha256sum /root/evidence/kw.bin
file /root/evidence/kw.bin
실제 재현 출력이다.
① 위장 프로세스 탐지
$ ps -eo pid,ppid,comm,args | awk '$3 ~ /^\[/ && $2!=2 && $2!=0'
1275 1 kw [kworker/0:2] -c import socket,time s=socket.socket(); ... bind(("0.0.0.0",4444)) ...
진짜 커널 스레드(4편)는 PPID가 2(kthreadd)다. 그런데 이 프로세스는 PPID가 1이고, 인자에 Python 소켓 코드 가 그대로 보인다. 이름만 [kworker/0:2]일 뿐 커널 스레드가 아니다.
② 실제 정체
$ ls -l /proc/1275/exe
/proc/1275/exe -> /tmp/.cache/kw (deleted)
$ ls -l /proc/1275/cwd
/proc/1275/cwd -> /tmp/.cache
$ cat /proc/1275/comm
kw
$ grep -E '^(PPid|Uid|State)' /proc/1275/status
State: S (sleeping)
Uid: 0 0 0 0
PPid: 1
| 단서 | 의미 |
|---|---|
exe -> /tmp/.cache/kw (deleted) | 임시 경로에서 실행 후 파일 삭제 (11편) |
cwd -> /tmp/.cache | 작업 디렉터리가 숨김 임시 경로 |
Uid: 0 | root 권한 으로 실행 중 |
comm: kw | 실제 이름은 kw, ps가 보여준 [kworker/0:2]와 다름 |
③ 네트워크 연결
$ ss -tanp | grep 4444
LISTEN 0.0.0.0:4444 0.0.0.0:* users:(("kw",pid=1275,fd=3))
ESTAB 127.0.0.1:4444 127.0.0.1:59370 users:(("kw",pid=1275,fd=4))
업무와 무관한 4444 포트를 열고(LISTEN), 실제로 연결이 맺어져 있다(ESTAB). 실제 침해라면 Peer 주소가 외부 IP인지 확인해 C2 통신 여부를 판단한다.
④ 열린 자원 (lsof)
$ lsof -p 1275
kw 1275 root cwd DIR ... /tmp/.cache
kw 1275 root txt REG ... /tmp/.cache/kw (deleted)
kw 1275 root 3u IPv4 ... TCP *:4444 (LISTEN)
kw 1275 root 4u IPv4 ... TCP localhost:4444->localhost:59370 (ESTABLISHED)
ss(네트워크)와 lsof(파일+네트워크)가 서로를 교차 검증한다.
⑤ 증적 복구
$ cp /proc/1275/exe /root/evidence/kw.bin
$ sha256sum /root/evidence/kw.bin
f56a588548dd013906ae1dcd1b6faa417f4e204da634ff354840d9643e78ff9e /root/evidence/kw.bin
$ file -b /root/evidence/kw.bin
ELF 64-bit LSB executable, x86-64, ...
파일이 디스크에서 삭제되었어도 프로세스가 열고 있는 동안에는 /proc/PID/exe로 복사 할 수 있다(11편). 이 해시로 위협 인텔리전스를 조회하거나 다른 서버에서 같은 파일을 검색한다. (이번 재현에서는 python 바이너리를 복사한 것이라 해시가 python3와 같지만, 실제 분석에서는 이 해시가 곧 IOC가 된다.)
| 관찰 | 정상 | 의심 |
|---|---|---|
[대괄호] 이름 | PPID 2, exe 없음, 메모리 0 (4편) | PPID≠2, exe 존재, 소켓 보유 |
| 실행 경로 | /usr/bin, /usr/sbin | /tmp, /dev/shm, (deleted) |
| 실행 계정 | 서비스는 전용 계정 (20편) | 웹·임시 프로세스가 root |
| 네트워크 | 업무 포트만 LISTEN | 4444, 1337 등 비업무 포트, 외부 ESTAB |
| 부모 프로세스 | sshd→bash, systemd | httpd→sh, PPID 1인 쉘 (고아 프로세스) |
도구 간 결과가 어긋나면 더 강한 은닉(커널 루트킷, 2편)을 의심한다. 예: ps에는 없는데 /proc/<PID>는 있는 PID, ss에는 없는데 방화벽 로그에는 있는 연결.
① 증적 수집 메모리 상태, /proc/PID/exe 복사, ss/lsof 결과 저장, 해시
② 프로세스 동결 kill -STOP <PID> (종료가 아니라 멈춤 → 분석 시간 확보)
③ 통신 차단 출발지/목적지 IP 방화벽 차단 (38편)
④ 지속성 점검 cron, systemd 유닛, authorized_keys, SUID (22·25·30·38편)
⑤ 종료·격리 kill <PID> 또는 서버 격리
프로세스를 먼저 kill하면 /proc/PID가 사라져 증적을 잃는다. kill -STOP으로 멈춰 두고 수집을 끝낸 뒤 종료한다.
# 위장 커널 스레드
ps -eo pid,ppid,comm,args | awk '$3 ~ /^\[/ && $2!=2 && $2!=0'
# 삭제된 실행 파일로 도는 프로세스
sudo ls -l /proc/*/exe 2>/dev/null | grep -E '\(deleted\)|/tmp/|/dev/shm|memfd:'
# 비업무 LISTEN 포트와 외부 ESTAB
sudo ss -tanp state listening; sudo ss -tanp state established '( dst != 10.0.0.0/8 )'
[Event] 28편: devuser 로그인 성공 (무차별 대입 IP)
↓
[프로세스] 위장 [kworker/0:2] 발견 → PPID 1, exe /tmp/.cache/kw(deleted), root
↓
[네트워크] 4444 LISTEN + ESTAB
↓
[증적] exe 복사 + 해시, ss/lsof 저장 → IOC
↓
[대응] kill -STOP → IP 차단 → 지속성(cron·키) 점검 → 종료
/proc/PID/exe·열린 소켓·해시 는 위장이 어렵다.ps→/proc/PID→ss→lsof→sha256sum 순으로 PID 하나를 좁혀 간다./proc/PID/exe로 복구 한다.kill -STOP → 차단 → 지속성 점검 → 종료 순서. kill 먼저 하면 증거를 잃는다.다음 글 「30. Linux 명령어를 활용한 보안 점검」 은 Part 3의 마무리다. 21~29편의 명령어를 모아 한 번에 실행하는 보안 점검 스크립트 를 만들고, 계정·권한·SUID·네트워크·지속성·로그를 점검해 기준값과 비교하는 흐름을 정리한다.