LummaStealer 악성코드 소스코드 분석(2)
분석에 앞서 .NET 관련 공부
IL(Intermediate Language) : C# -> IL -> 기계어 순서로 컴파일됨. dnSpy가 이를 읽기 쉽게 C#으로 디컴파일해서 보여줌. 즉 IL은 .NET의 중간 언어
.NET 구조 :
- CLR(Common Language Runtime) : .NET의 실행 환경
- BCL(Base Class Library) : 기본 클래스 라이브러리
- JIT(Just-In-Time) 컴파일러 : 실행 시점에 IL을 기계어로 변환
난독화 해제 과정
1) de4dot.exe라는 난독화 해제 툴을 사용하여 해제를 시도하였으나, ResolutionScope is null으로 에러가 났다. dnSpy를 확인해 봐도 해제 시도 전과 코드가 다를 바 없었음.
2) 난독화 타입 자동 감지 기능이 있대서 시도해봄. 그러나 detected unknown이라는 결과가 나옴....
그래서 아래 코드를 하나하나 써봄.
de4dot.exe -d 원본파일.exe
de4dot.exe --strtyp delegate 원본파일.exe
de4dot.exe 원본파일.exe
정확히 어떤 코드 덕분에 성공했는지는 몰라도 아래처럼 LummaStealer원본 파일명 뒤에 cleaned가 붙은 파일이 생성됨. 크기가 줄어들긴 했으나 일부는 난독화 해제된 것으로 보임.
dnSpy로 이 파일을 열어본 결과 난독화가 정상적으로 해제된 것을 확인할 수 있었다.
난독화 해제된 코드 분석 과정(1) - 암호화/복호화 및 코드 동적생성
우선 password를 키워드로 검색을 해보았다. get_Password라는 수상한 이름이 보여 더블클릭해 확인한다. 하지만 이런 식으로 확인한 코드들은 전부 Windows Forms PasswordBox로 악성코드는 아닌 것으로 보였다.
이외에도 다른 키워드(chrome, browser 등과 같은)를 검색해 보았으나 특이사항을 발견하기 어려워, 3.exe 파일부터 분석해 보기로 했다.
ns0의 main 함수 - delegate 16의 smethod_0 - Class122를 순서대로 타고들어가 살펴보았다.
*delegate : 함수 포인터(다른 함수를 가리키는 변수), 정적 분석을 어렵게 만든다.
11번째 줄 : 네임 스페이스
127번째 줄 : RSA 암호화
151~204번째 줄 : 복잡한 바이트 조작 및 복호화 루틴
정리하면 class122는 암호화/복호화 엔진, 데이터 변환 및 처리를 맡음. 이런 식으로 대략적으로 훑어보고, class122를 전부 확인하기에는 양이 너무 방대해서 delegate 16에서 class122와 함께 확인했던 smethod_13을 분석해 본다.
475~477번째 줄 : 리소스 읽기
BinaryReader binaryReader = new BinaryReader(typeof(Class122).Assembly.GetManifestResourceStream("#u0097n#u0945#u009e#u0091#u0094pxddq63#u008e#u0085..."));
580~607번째 줄 : 동적 메소드 생성 -> IL(Intermediate Language) 코드 동적 생성, 런타임에 실제 악성 기능 생성
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, methodInfo.ReturnType, array3, typeFromHandle, true);
ILGenerator ilgenerator = dynamicMethod.GetILGenerator();
588~605번째 줄 : OpCodes -> IL 바이트코드 직접 생성, 실제 악성 함수를 메모리에서 동적 구성
-> 암호화된 리소스에서 악성코드를 복호화한 후 동적으로 메모리에 로드한다. 그리고 실행 준비를 마친다...
그럼 이제 동적으로 생성된 함수들이 호출되는 곳, 탈취 대상, 그리고 네트워크 통신과 관련한 코드를 찾아내야 한다.
난독화 해제된 코드 분석 과정(2) - 네트워크 통신
http를 검색해 목록을 살펴보다, 이름이 CreateHttpWebRequest인 함수를 발견했다.
290~302번째 줄 : HTTP 요청 생성
private HttpWebRequest CreateHttpWebRequest(int[] byteRanges)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.CreateHttpMethod(this._requestUri);
httpWebRequest.ProtocolVersion = HttpVersion.Version11;
httpWebRequest.Method = "GET";
httpWebRequest.Proxy = this._proxy;
httpWebRequest.Credentials = this._credentials;
httpWebRequest.CachePolicy = this._cachePolicy;
}
- GET 요청 생성
- proxy를 통한 통신
- credentials를 통한 인증정보 설정
- byteRange를 통한 파일 특정 부분 다운로드
*프록시(proxy) : 서버와 클라이언트 사이의 중계 역할
이는 다운로드 기능으로 보이며, 데이터 전송하는 부분을 찾아야 할 것 같다.
http를 다시 검색해 이름이 CreateHttpClient인 함수를 발견했다.
1273~1275번째 줄 : 쿠키 설정 -> 쿠키 조작 가능성
1281~1297번째 줄 : 프록시 설정 -> 프록시 우회로 네트워크 필터링 회피 가능성
1293~1304번째 줄 : SSL/TLS 인증서 무시 -> 가짜 인증서도 허용, C2 서버가 정식 인증서 없이도 통신 가능 -> 보안을 의도적으로 약화시켜 탐지를 회피
httpClientHandler.ClientCertificates.AddRange(parameters.ClientCertificates);
httpClientHandler.SslProtocols = (SslProtocols)parameters.SslProtocols;
httpClientHandler.CheckCertificateRevocationList = parameters.CheckCertificateRevocationList;
이러한 코드는 C2 서버통신을 위한 HTTP 클라이언트 설정이다.
마찬가지로 request http를 키워드로 검색하다가 SendContentAsync라는 함수를 발견했다. 아마 이게 데이터 전송하는 함수인 것 같다.
124~131번째 줄 : 실제 데이터 전송
Http3RequestStream.<SendContentAsync>d__28 <SendContentAsync>d__;
<SendContentAsync>d__.content = content;
<SendContentAsync>d__.cancellationToken = cancellationToken;
<SendContentAsync>d__.<>4__this = this;
<SendContentAsync>d__.<>t__builder.Start<Http3RequestStream.<SendContentAsync>d__29>(ref <SendContentAsync>d__);
135번째 줄 : 데이터 쓰기
private ValueTask WriteRequestContentAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
해당 함수를 사용한 곳을 찾기 위해 analyzer를 사용해 봤다.
Used By 항목을 보면, System.Net.Http.Http3RequestStream.SendAsync에서 사용한 것을 확인할 수 있다. 그러나 막상 SendAsync 함수에서 SendContentAsync 함수가 호출된 흔적이 보이지 않아, Analyzer가 잘못된 것 같기도 하다..
난독화 해제된 코드 분석 과정(3) - 탈취 대상
steal을 키워드로 검색하여 의심스러운 함수를 찾았다. TrySteal이라는 이름의 함수이다.
507번째 줄 : 훔칠 수 있는지 확인, CanSteal 속성이 true가 될 때까지 대기
515~520번째 줄 : 인덱스 읽고 계산 후 데이터 읽기 -> 큐에 저장된 데이터를 빼내옴
525~526번째 줄 : 훔친 데이터를 배열에서 삭제, 성공적으로 훔친 데이터를 호출자에게 반환
this.m_array[num] = null
return obj
다만 이 경우, 정상적인 work stealing queue 로직처럼 보임. 스레드 기반 데이터 탈취 가능성 있음..
(여기서부터 미완성..)