
이벤트 뷰어 역직렬화 UAC 우회의 Beacon Object File 구현
이것은 @orange_8361이 발견하고 CsEnox가 POC를 구성한 Event Viewer 역직렬화 UAC 우회의 Beacon Object File 구현입니다.

x64 Win10/Win11에서 테스트됨
이 UAC 우회는 다음 작업을 수행하므로 OPSEC과 관련하여 고려해야 합니다:
-1. %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews에 바이너리 파일을 씁니다.
-2. ShellExecute()를 호출하여 mmc.exe / Event Viewer를 시작합니다.
--A. Event Viewer가 GUI로 열립니다!
--B. Event Viewer가 cmd.exe를 호출합니다.
---a. Cmd.exe가 mmc.exe를 종료하기 위해 Taskkill을 호출합니다.
---b. Cmd.exe가 가끔 열려 있는 콘솔 창에 오류 메시지를 출력하여 사용자에게 아무 문제가 없다고 믿게 합니다.
---c. Cmd.exe가 사용자가 제공한 다른 명령을 실행합니다.
-3. 바이너리 파일이 %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews에서 삭제됩니다.
원본 데모와 CsEnox의 POC에 따라 ysoserial을 사용하여 초기 페이로드를 생성했습니다:

ysoserial.exe -o raw -f BinaryFormatter -g TextFormattingRunProperties -c "taskkill /f /im mmc.exe >nul && echo Windows has Recovered from an Unexpected Error. You may Close this Window. &&" > cmd.bin
이 UAC 우회는 mmc.exe를 호출하여 작동하며, mmc.exe는 화면에 열리고 닫혀야 합니다. 불행히도 GUI를 열지 않고 mmc.exe를 시작하는 방법을 찾지 못했습니다; CreateProcess()와 CREATE_NO_WINDOW 플래그를 살펴보았지만, CreateProcess()는 UAC와 잘 작동하지 않습니다. 이 주제에 대한 StackOverflow 게시물은 SW_HIDE 플래그(및 기타)가 있는 ShellExecuteA() API를 알려주었지만, 이는 효과적이지 않았습니다. mmc.exe가 화면에 나타나는 것은 피할 수 없는 것 같습니다.
Ysoserial은 cmd.exe /c를 호출하므로 이를 사용하여 mmc.exe를 종료하기 위해 taskkill을 호출합니다. &&를 사용하여 명령을 연결하여 관리자로 수행하려는 작업을 실행할 수 있습니다. 테스트에서 어떤 경우에는 그 작업이 mmc.exe가 생성한 cmd.exe 프롬프트를 열려 있게 유지한다는 것을 발견했습니다; 이는 기본 Cobalt Strike 실행 파일 페이로드와 같은 로컬 인젝터(일반) 페이로드를 호출할 때 발생합니다. 이를 해결하기 위해 taskkill 명령의 출력(이전에는 열려 있는 cmd.exe에 출력됨)을 /dev/null로 보내고, 처리된 오류에 대한 메시지를 화면에 echo하여 이 의심스러운 동작이 교육받지 않은 사용자를 놀라게 하지 않도록 했습니다.
프로세스를 생성하고, 그 프로세스에 인젝션한 후 종료하는 페이로드는 cmd.exe 창을 열어 두지 않으므로 메시지가 필요하지 않습니다. 마찬가지로 실행 후 종료되는 명령(예: net user Administrator newpassword)도 창을 열어 두지 않습니다.
이 BOF를 통해 powershell 인코딩 명령을 실행할 수 있습니다:



taskkill 명령과 사용자에게 보내는 메시지는 ysoserial 명령에 포함되어 있습니다. 이 동작을 변경하려면 자체 ysoserial 명령을 실행하고 아래 코드 생성 방법 데모를 따라야 합니다.
사용자 정의 도구를 사용하여 바이너리 파일을 읽어서 16진수로 출력했습니다:

이것을 메모장에 복사하고 16진수에서 '&'를 검색하면 원하는 바이트를 찾을 수 있습니다 (이러한 세트가 여러 개 있으므로 마지막 것이 필요합니다):

페이로드는 이 바이트 직후에 두 부분으로 나뉩니다:

각 부분의 길이를 계산하여 나중에 기록합니다.
여기서부터 복잡해집니다.
사실 역직렬화나 ysoserial에 대해 잘 알지 못하지만, 페이로드에 사용자 정의 명령을 포함한 페이로드의 "중요한" 부분의 길이를 나타내는 바이트 집합이 있다는 것을 발견했습니다. 이는 서로 다른 명령을 포함하는 ysoserial로 생성된 두 개의 다른 페이로드의 나란히 비교한 16진수 덤프 스크린샷에서 볼 수 있습니다:

설명할 수 없는 이유로 이 바이트들은 base-128로 페이로드의 길이를 반영합니다; 0x05에 128을 곱하면 640이 됩니다; 이 값에 다른 바이트(0xb0 또는 0xd4)의 값에서 128을 뺀 값을 더합니다; 두 번째 바이트(0x05)가 오버플로우(0x06)되면 첫 번째 바이트가 예상되는 0x00 대신 0x80에서 다시 시작한다는 것을 발견했습니다.
이 도구에서 사용된 페이로드는 다음과 같습니다:

모든 값을 10진수로 변환하면 계산은 다음과 같습니다:
(6 x 128) + (180-128) = 820
파일 크기는 1042입니다; 테스트 결과 (이 ysoserial 가젯에서는) 파일 크기와 이 바이트 집합에 반영된 길이 사이에 항상 222바이트 차이가 있음을 발견했습니다. 이 222바이트는 페이로드의 '헤더'로 보입니다.
페이로드의 두 부분은 main.c에 복사되며, 여기서 원래 길이 820도 변수로 설정됩니다. 사용자 명령의 길이를 계산하고, 이 값을 원래 820에 추가하여(삽입해야 하는 공백을 위한 추가 1바이트 포함) 이 바이트 집합에 표현해야 하는 최종 값을 계산합니다. 두 번째 바이트는 LenBytes를 128로 나누어 계산하고, 첫 번째 바이트는 LenBytes의 나머지를 구하고 128을 더하여 계산합니다(0x80에서 시작하는 방식을 수용하기 위해).

이 바이트들이 설정되면, 내용에 따라 페이로드의 길이를 적절한 값으로 설정하기 위해 적절한 위치의 Before 버퍼에 복사됩니다. 그런 다음 최종 버퍼가 조립됩니다:

완성된 페이로드는 디스크에 기록되고 mmc.exe가 호출되어 실행됩니다. 이후 페이로드는 정리됩니다.
x86_64-w64-mingw32-gcc -o EventViewerUAC.x64.o -Os -c main.c -DBOF
Ysoserial은 --rawcmd 옵션을 제공하여 cmd.exe 외의 다른 실행 파일을 호출할 수 있지만, 인수를 제공할 수는 없습니다. 이를 통해 cmd.exe를 호출하지 않고 페이로드를 직접 호출할 수 있어 콘솔 창이 열려 있지 않게 됩니다; 그러나 이 경우 mmc.exe를 닫을 방법이 없습니다. 페이로드의 부모 프로세스를 확인하고 mmc.exe인 경우 mmc.exe를 종료하여 이 UAC 우회 후 정리하도록 페이로드를 작성할 수 있습니다. 이는 매우 가능하지만, 이는 많은 UAC 우회 중 하나이고 모든 사람이 그런 사용자 정의 페이로드를 만드는 것은 아니므로 번거로움을 감수하지 않았습니다. 개인 러너에 통합할 수도 있습니다.