Secure mail

f12 눌러서 개발자모드 들어가본다

obfuscation 되어 있다. js 난독화는 유명하니까 web으로도 deobfus를 할 수 있는데 유의미한 함수가 나오진 않았다.

하나하나 분석하는 문제는 아닌 거 같고 6자리밖에 안되니까 BF로 풀어준다.

// 브루트포스 함수 정의
async function bruteforcePassword() {
  // 알림 핸들러 설정
  let isWrong = false;
  const originalAlert = window.alert;
  
  window.alert = function(message) {
    if (message === 'Wrong') {
      isWrong = true;
      // 알림 창 자동으로 닫기 (확인 버튼 클릭)
      setTimeout(() => {
        const alertButton = document.querySelector(".alert-button") || 
                           document.querySelector("[data-dismiss='alert']") || 
                           document.querySelector(".close");
        if (alertButton) alertButton.click();
      }, 100);
    } else {
      // Wrong이 아닌 다른 알림이 뜨면 원래 alert 함수 호출하고 브루트포스 중단
      originalAlert(message);
      console.log("패스워드 발견: " + inp);
      window.alert = originalAlert; // 원래 alert 함수 복원
      return true; // 성공 신호 반환
    }
    return false;
  };

  // 연도 범위: 1980(80)~2024(24)
  for (let year = 80; year <= 99; year++) {
    for (let month = 1; month <= 12; month++) {
      for (let day = 1; day <= 31; day++) {
        // 유효하지 않은 날짜 건너뛰기 (간단한 검증)
        if ((month === 4 || month === 6 || month === 9 || month === 11) && day > 30) continue;
        if (month === 2) {
          // 윤년 계산 (간단한 버전)
          const fullYear = year + 1900;
          const isLeapYear = (fullYear % 4 === 0 && fullYear % 100 !== 0) || (fullYear % 400 === 0);
          if (day > (isLeapYear ? 29 : 28)) continue;
        }
        
        // 생년월일 형식으로 변환 (YYMMDD)
        const yearStr = year.toString().padStart(2, '0');
        const monthStr = month.toString().padStart(2, '0');
        const dayStr = day.toString().padStart(2, '0');
        const inp = yearStr + monthStr + dayStr;
        
        console.log("시도 중: " + inp);
        
        // 입력 필드에 값 설정하고 제출 버튼 클릭
        document.querySelector("input[id=pass]").value = inp;
        document.querySelector("button[type='submit']").click();
        
        // 응답 대기
        await new Promise(resolve => setTimeout(resolve, 500));
        
        // Wrong 알림이 아닌 다른 결과가 나왔다면 성공으로 간주하고 중단
        if (!isWrong) {
          console.log("패스워드 발견: " + inp);
          window.alert = originalAlert; // 원래 alert 함수 복원
          return;
        }
        
        isWrong = false; // 다음 시도를 위해 초기화
      }
    }
  }
  
  console.log("모든 가능한 패스워드를 시도했지만 찾지 못했습니다.");
  window.alert = originalAlert; // 원래 alert 함수 복원
}

// 브루트포스 시작
bruteforcePassword();

코드는 지피티 도움 받았고 이걸 console에 입력했다

열심히 기다리면

발견

발견

결과

lv1 치고 오래 걸리지 않나..

pico- WinAntiDBG0x300

Image 1

admin으로 실행해봄
dbg 쓰지 말라고 했지만 써봐야징

Image 2

일케 뜸

Image 3

f9 계속하면 일케 뜸

Image 4

메모리랩 갔는데 UPX

Image 5

일단 풀어줌

Image 6

디버거 탐지를 하니까 아이다로 일단 정적 분석만 진행

Image 7

문자열 먼저 찾았다.

Image 8

여기가 플래그 출력하는 함수이니까 여길 참조하는

Image 9

위 함수가 플래그 계산하는 함수일 것
최대한 여기로 분기하도록 막 설정했더니 프로그램 자체가 안 열렸다;;

Image 10

이리저리 패치했는데 모르겠어서 일단 라업 봤다
무엇보다 startadress의 출처를 모르겠다
라업을 봤는데 offset으로 해서 스레드 호출을 이렇게 표현한다고 하더라(아래 참고)

Image 11

라업은 디버거 중단점 설정하면서 바꿨는데 나는 아예 패치 써보려고 jz 또는 jnz로 있던 분기문을 무조건 분기인 jmp로 바꾸고 패치했다.
아이다로 패치할 때 edit>patch program.. 들어가면 다 있다.

Image 12

SECCON 2016- Anti-Debugging

// positive sp value has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // eax
  HANDLE CurrentProcess; // eax
  int v6; // [esp+C4h] [ebp-A8h]
  DWORD TickCount; // [esp+D4h] [ebp-98h]
  LPCSTR lpFileName; // [esp+D8h] [ebp-94h] BYREF
  BOOL pbDebuggerPresent[6]; // [esp+DCh] [ebp-90h] BYREF
  char Str2[16]; // [esp+F8h] [ebp-74h] BYREF
  int v11; // [esp+108h] [ebp-64h]
  char Str1[68]; // [esp+10Ch] [ebp-60h] BYREF
  CPPEH_RECORD ms_exc; // [esp+154h] [ebp-18h]

  memset(Str1, 0, 64);
  v11 = 1;
  printf("Input password >");
  v3 = (FILE *)sub_40223D();
  fgets(Str1, 64, v3);
  strcpy(Str2, "I have a pen.");
  v11 = strncmp(Str1, Str2, 0xD);
  if ( !v11 )
  {
    puts("Your password is correct.");
    if ( IsDebuggerPresent() )
    {
      puts("But detected debugger!");
      exit(1);
    }
    if ( sub_401120() == 112 )
    {
      puts("But detected NtGlobalFlag!");
      exit(1);
    }
    CurrentProcess = GetCurrentProcess();
    CheckRemoteDebuggerPresent(CurrentProcess, pbDebuggerPresent);
    if ( pbDebuggerPresent[0] )
    {
      printf("But detected remotedebug.\n");
      exit(1);
    }
    TickCount = GetTickCount();
    pbDebuggerPresent[3] = 0;
    pbDebuggerPresent[1] = 1000;
    if ( GetTickCount() - TickCount > 0x3E8 )
    {
      printf("But detected debug.\n");
      exit(1);
    }
    lpFileName = "\\\\.\\Global\\ProcmonDebugLogger";
    if ( CreateFileA("\\\\.\\Global\\ProcmonDebugLogger", 0x80000000, 7u, 0, 3u, 0x80u, 0) != (HANDLE)-1 )
    {
      printf("But detect %s.\n", (const char *)&lpFileName);
      exit(1);
    }
    v6 = sub_401130();
    switch ( v6 )
    {
      case 1:
        printf("But detected Ollydbg.\n");
        exit(1);
      case 2:
        printf("But detected ImmunityDebugger.\n");
        exit(1);
      case 3:
        printf("But detected IDA.\n");
        exit(1);
      case 4:
        printf("But detected WireShark.\n");
        exit(1);
    }
    if ( sub_401240() == 1 )
    {
      printf("But detected VMware.\n");
      exit(1);
    }
    pbDebuggerPresent[2] = 1;
    pbDebuggerPresent[5] = 1;
    pbDebuggerPresent[4] = 1 / 0;
    ms_exc.registration.TryLevel = -2;
    printf("But detected Debugged.\n");
    exit(1);
  }
  printf("password is wrong.\n");
  return 0;
}

입력값도 다 있음 겁나게 우회하면 되겠군 생각함

스크린샷 2025-04-12 150741

프롬프트에 I have a pen. 입력하고

스크린샷 2025-04-12 150741

겁나게 우회

근데 어디선가 디버거를 잡는겨

스크린샷 2025-04-12 150741

이쯤임

그래서 ida로 다시 봐봄

스크린샷 2025-04-12 150741

수많은 디버거 우회를 하면 이쯤에서 도달하는 거 같음 여기가 아마 flag 번역하는 부분

그래서 그냥 저 aAjJq7 머시기를 문자열 참조로 찾고 그위쯤 주소를 jmp하기로 함

스크린샷 2025-04-12 152536

맨처음 디버거 우회 함수가 IsDebuggerPresent머시기

여기 아래가 원래 jne 머시기였는데

je bin.40165D로 바꿔줌

스크린샷 2025-04-12 152523

여기서 계속 루프 돌면서 flag를 만들고 있음

뭔가 루프가 많이 도는 거 같아서

스크린샷 2025-04-12 152518

여기에 BP 걸고 f9

스크린샷 2025-04-12 152554

그리고 메세지박스 호출까지 f8해주면 이렇게 뜬다

사실 디버거 함수 중간중간에 flag decode가 있었으면 귀찮게 또 우회우회할 텐데 다행히 걍 분기해도 플래그가 나오는 문제였다.

pico-rev_cipher

int __fastcall main(int argc, const char **argv, const char **envp)
{
  _BYTE inputbyte[23]; // [rsp+0h] [rbp-50h] BYREF
  char v5; // [rsp+17h] [rbp-39h]
  int v6; // [rsp+2Ch] [rbp-24h]
  FILE *output; // [rsp+30h] [rbp-20h]
  FILE *input; // [rsp+38h] [rbp-18h]
  int j; // [rsp+44h] [rbp-Ch]
  int i; // [rsp+48h] [rbp-8h]
  char v11; // [rsp+4Fh] [rbp-1h]

  input = fopen("flag.txt", "r");
  output = fopen("rev_this", "a");
  if ( !input )
    puts("No flag found, please make sure this is run on the server");
  if ( !output )
    puts("please run this on the server");
  v6 = fread(inputbyte, 0x18u, 1u, input);
  if ( v6 <= 0 )
    exit(0);
  for ( i = 0; i <= 7; ++i )
  {
    v11 = inputbyte[i];
    fputc(v11, output);
  }
  for ( j = 8; j <= 22; ++j )
  {
    v11 = inputbyte[j];
    if ( (j & 1) != 0 )
      v11 -= 2;
    else
      v11 += 5;
    fputc(v11, output);
  }
  v11 = v5;
  fputc(v5, output);
  fclose(output);
  return fclose(input);
}

1~8글자까지는 inputbyte 그대로
9~23글자까지는
j가 짝수일 때 v11+=5
j가 홀수일 때 v11-=2

이걸 리버싱하는 코드를 짜면 되겠군

rev_this output

일단 rev_this 하면 위와 같다.

revthis="picoCTF{w1{1wq85jc=2i0<}"
flag=""

for i in range(0,8):
    flag+=revthis[i]

for i in range(8, 23):
    char = revthis[i]
    if(i % 2 != 0):
        flag = flag + chr(ord(char) + 2)
    else:
        flag = flag + chr(ord(char) - 5)
print(flag)

print(flag)

닫는 괄호 추가해서 solve

ELF x86 - Ptrace

elf라 일단 gdb로 바로 까고 disass 출력했다.
ptrace가 있다.
catchpoint 잡고 commands 만들면 쉽긴 한데 여기서 continue가 왜인지 오류가 나더라……..
그래서 일단

start 
disass //명령어 보면서 ptrace 부르는 함수 주소 파악 
b *main+32 
r
ni //ptrace 일단 실행 한 번 시키고 
set $eax=0 //반환값 0으로 고치기 

//사실 그냥 ptrace 다음 명령에 bp 걸고 eax=0 걸면된다

이렇게 했더니 password 입력창이 나온다.
쉬운 문제면 문자열에 있으니까 ida로 까봤는데 아니다 이거 gdb로 분석을 해야할 거 같다.

잘 보면 dl과 al을 비교하여 틀리면 0x80484e4로 간다.

0x80484e4를 가보면 위와 같다. push하는 값이 뭔지 참조해보면

jne로 분기하면 안되는 구간이다.
결론: cmp로 dl, al을 모두 맞춰줘야 한다.
cmp 하는 구간에 bp를 걸고 가보면

이렇게 al 값이 누출된다. (내가 작성한 값은 dl)
disass를 다시 보면 cmp를 총 4번한다(4글자)
이정돈 노가다를 해보자

해독했더니 easy가 나왔다.