Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagecpp
titlemain
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed int state; // [rsp+4h] [rbp-Ch]

  state = 1;
  signal(14, handler);
  alarm(0x1Eu);
  title();
  setCandy();
  gOrderCnt = 0;
  gLoginFailCnt = 0;
  while ( !gLoginAccount )
  {
    if ( (unsigned int)login() )
    {
      gLoginFailCnt = 0;
LABEL_14:
      while ( state && gLoginAccount )
      {
        Menu();
        printf("Command : ");
        switch ( (unsigned int)retNumber(2LL) )
        {
          case 0u:
            state = 0;
            break;
          case 1u:
            printStock();
            break;
          case 2u:
            purchase();
            break;
          case 3u:
            charge();
            break;
          case 4u:
            if ( gLoginAccount->state == 1 )
              orderMenu();
            break;
          case 5u:
            if ( gLoginAccount->state == 1 )
              Account();
            break;
          case 9u:
            logout(2LL);
            break;
          default:
            goto LABEL_14;
        }
      }
    }
    else
    {
      if ( gLoginFailCnt == 2 )
        exit(1);
      ++gLoginFailCnt;
      puts("\nCreate an account?");
      puts("0) Yes\n1) No");
      if ( !(unsigned int)retNumber(2LL) )
        addAccount(3LL);
    }
  }
  return 0LL;
}

...

login()


  • 해당 함수는 다음과 같은 기능을 합니다.
  • 해당 함수는 전역 변수 gAccount[].state 의 값이 '0' 인 경우 다음과 같이 동작합니다.
    • 해당 함수는 malloc()을 사용하여 128 byte의 heap 영역을 할당받습니다.

      • 해당 함수는 해당 영역의 주소를 gAccount[i].fd에 저장합니다.

      • 해당 함수는 해당 영역에 ID, Password,등의 정보를 저장합니다.
    사용자로 부터 ID,Password를 입력 받습니다.
  • 해당 함수는 입력 받은 값을 전역 변수 gAccount[]에 존재하는지 확인합니다.



Code Block
signed
Code Block
languagecpp
titleaddAccount(unsigned int a1)
unsigned __int64 __fastcall addAccount(unsigned int a1login()
{
  unsigned size_t lenUserInputID; // rbx
  size_t lenID; // rax
  size_t lenUserInputPW; // rbx
  size_t lenPW; // rax
  signed int i; // [rsp+10hCh] [rbp-10h34h]
  signed int emptychar id[8]; // [rsp+14h10h] [rbp-Ch30h]
  unsigned __int64 v4char pw[8]; // [rsp+18h20h] [rbp-8h20h]

  v4 =unsigned __readfsqword(0x28u);
  empty = 1int64 v8; // [rsp+28h] [rbp-18h]

  v8 = __readfsqword(0x28u);
  for memset(id, i = 0; i <= 2 && empty; ++i )
  {
    if ( !gAccount[i].state )
    {
      empty0, 8uLL);
  memset(pw, 0, 8uLL);
  printf("\nEnter your ID.\n> ", 0LL);
  UserInput(id, 8LL);
  printf("Enter your Password.\n> ", 8LL);
  UserInput(pw, 8LL);
  for ( i = 0;
 i <= 2;   gAccount[i].state = a1;++i )
  {
    if ( gAccount[i].numberstate )
 = i + 1;{
      gAccount[i].fdlenUserInputID = (struct IDPW *)malloc(128uLLstrlen(id);
      if ( lenUserInputID == strlen(gAccount[i].fd->state>id) =)
 1LL;
     {
 puts("\nEnter your New ID.");
    lenID = UserInputstrlen(gAccount[i].fd->id, 8LL);
      puts("Enter your New Password.");
      UserInputif ( !strncmp(gAccount[i].fd->pw, 8LL);>id, id, lenID) )
      puts("Enter your profile.");
{
          lenUserInputPW = UserInput(gAccount[i].fd->description, 88LLstrlen(pw);
         gAccount[i].bk = 10000LL;
    }
  }
  if ( empty )
    puts("Could not add user.");
  return __readfsqword(0x28u) ^ v4;
}
  • 해당 함수는 다음과 같은 구조체를 사용합니다.
Code Block
struct IDPW{
    long empty[2] if ( lenUserInputPW == strlen(gAccount[i].fd->pw) )
          {
            lenPW = strlen(gAccount[i].fd->pw);
    char id[8];
    char pw[8];
   if ( !strncmp(gAccount[i].fd->pw, pw, lenPW) )
     long state;
    char description[88];
};

struct ACCOUNT{
      long state;
    long number;
  gLoginAccount = (struct IDPW *fd;
 ACCOUNT *)(32LL * i + 0x604220);
             long bk;
};

delAccount

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 전역 변수 gAccount[]를 이용해 삭제 가능한 계정들을 출력합니다.
    • 해당 함수는 사용자로 부터 삭제 할 계정의 번호를 입력 받습니다.
    • 해당 함수는 사용자가 선택한 계정의 state 정보가 '3' 일 경우 삭제를 진행합니다.

      • 해당 함수는 해당 계정(gAccount[num])의 정보를 초기화 합니다.

        • state = 0

        • fd→state = 0

        • memset(gAccount[num].fd, 0, 0x80uLL);

      • 해당 함수는 해당 계정(gAccount[num])의 fd 영역(heap)을 해제 합니다.

    •  해당 함수는 gAccount[num].fd 영역에 "gAccount[num].fd - 16" 연산 한 값을 저장합니다.

      • 저장되는 값은 Free chunk의 Head 주소입니다.
      • 이것으로 인해 The House of Lore 취약성이 발생하게 됩니다.
 printf("\nHi, %s", gAccount[i].fd->id);
              return 1LL;
            }
          }
        }
      }
    }
  }
  return 0LL;
}



addAccount()

  • 해당 함수는 다음과 같은 기능을 합니다.

    • 해당 함수는 전역 변수 gAccount[].state 의 값이 '0' 인 경우 다음과 같이 동작합니다.

      • 해당 함수는 malloc()을 사용하여 128 byte의 heap 영역을 할당받습니다.

        • 해당 함수는 해당 영역의 주소를 gAccount[i].fd에 저장합니다.

        • 해당 함수는 해당 영역에 ID, Password,등의 정보를 저장합니다.
Code Block
languagecpp
titleaddAccount(unsigned int a1)
unsigned __int64 __fastcall addAccount(unsigned int a1)
{
  unsigned int i; // [rsp+10h] [rbp-10h]
  signed int empty; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  empty = 1;
  for ( i = 0; i <= 2 && empty; ++i )
  {
    if ( !gAccount[i].state )
    {
      empty = 0
Code Block
languagecpp
titledelAccount()
unsigned __int64 delAccount()
{
  unsigned int i; // [rsp+8h] [rbp-18h]
  unsigned int num; // [rsp+Ch] [rbp-14h] MAPDST
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts("\nAccount list");
  for ( i = 0; i <= 2; ++i )
  {
    if ( gAccount[i].state )
      printf("%d) %s\n", gAccount[i].number, gAccount[i].fd->id);
  }
  puts("\nPlease enter the number of the account you want to delete");
  num = retNumber(2LL);
  if ( num && num <= 3 )
  {
    if ( gAccount[--num].state == 3 )
    {
      gAccount[num].state = 0LL;
      gAccount[num].fd->state = 0LL;
      printf("The account(%s) has been deleted.\n", gAccount[num].fd->id);
      memset(gAccount[numi].fd,state 0,= 0x80uLL)a1;
      free(gAccount[numi].fd)number = i + 1;
      gAccount[numi].fd = (struct IDPW *)((char *)gAccount[nummalloc(128uLL);
      gAccount[i].fd->state -= 16)1LL;
    }
    else
    {puts("\nEnter your New ID.");
      UserInput(gAccount[i].fd->id, 8LL);
      puts("YouEnter canyour not delete the accountNew Password.");
     }
  }
  return __readfsqword(0x28u) ^ v4;
}

printStock

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 전역 변수 gStock를 이용해 사탕 정보를 출력합니다.
Code Block
languagecpp
titleprintStock()
unsigned __int64 printStock()
{
  unsigned int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 =  UserInput(gAccount[i].fd->pw, 8LL);
      puts("Enter your profile.");
      UserInput(gAccount[i].fd->description, 88LL);
      gAccount[i].bk = 10000LL;
    }
  }
  if ( empty )
    puts("Could not add user.");
  return __readfsqword(0x28u) ^ v4;
}
  • 해당 함수는 다음과 같은 구조체를 사용합니다.
Code Block
struct IDPW{
  if ( gStockCnt )long empty[2];
  {
  char id[8];
 for ( i = 0; i < gStockCnt; ++i )
    char pw[8];
    long state;
    char description[88];
};

struct ACCOUNT{
    long  printf("\n=*= Candy %d =*=\n", i)state;
    long number;
    struct  printf("Name of candy        : %s\n", gStock[i])IDPW *fd;
      printf("Number of candy      : %d\n", gStock[i]->candyNumber);
      printf("Price of candy       : %d\n", gStock[i]->candyPrice);
      printf("Description of candy : %s\n", gStock[i]->candyDescription);
    }
  }
  else
  {
    puts("We have not any candy.");
  }
  return __readfsqword(0x28u) ^ v2;
}long bk;
};

delAccount

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 전역 변수 gAccount[]를 이용해 삭제 가능한 계정들을 출력합니다.
    • 해당 함수는 사용자로 부터 삭제 할 계정의 번호를 입력 받습니다.
    • 해당 함수는 사용자가 선택한 계정의 state 정보가 '3' 일 경우 삭제를 진행합니다.

      • 해당 함수는 해당 계정(gAccount[num])의 정보를 초기화 합니다.

        • state = 0

        • fd→state = 0

        • memset(gAccount[num].fd, 0, 0x80uLL);

      • 해당 함수는 해당 계정(gAccount[num])의 fd 영역(heap)을 해제 합니다.

    •  해당 함수는 gAccount[num].fd 영역에 "gAccount[num].fd - 16" 연산 한 값을 저장합니다.

      • 저장되는 값은 Free chunk의 Head 주소입니다.
      • 이것으로 인해 The House of Lore 취약성이 발생하게 됩니다
    다음과 같은 구조체를 사용합니다
      • .
Code Block
languagecpp
titlestruct STOCKdelAccount()
unsigned __int64 delAccount()
struct STOCK{
  unsigned  char candyName[8];
   int i; // [rsp+8h] [rbp-18h]
  unsigned int  candyNumber;num; // [rsp+Ch] [rbp-14h] MAPDST
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4  unsigned int  candyPrice;
    char *candyDescription;
};

purchase

  • 해당 함수는 다음과 같은 기능을 합니다.

    • 해당 함수는 전역 변수 gStockCnt의 값이 0일 경우 메시지 출력 해당 기능을 종료합니다.

    • 해당 함수는 전역 변수 gStockCnt의 값이 0이 아닐 경우 다음과 같은 기능을 합니다.

      • 해당 함수는 사용자로 부터 구매 할 캔디의 코드 번호(candyInfo[0]), 캔디의 수(candyInfo[1])를 입력받습니다.

        • 해당 함수는 구매 가능한 캔디 코드 번호, 캔디의 개수인지 확인합니다.

      • 해당 함수는 정상적인 구매가 진행 되며, 캔디의 재고가 없을 경우 아래 함수가 호출됩니다.

        • reSortStock()

        • setBoard()

Code Block
languagecpp
titlepurchase()
unsigned __int64 purchase()
{
  unsigned int v0; // ST00_4
  unsigned int candyInfo[2]; // [rsp+0h] [rbp-10h]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u= __readfsqword(0x28u);
  puts("\nAccount list");
  for ( i = 0; i <= 2; ++i )
  {
    if ( gAccount[i].state )
      printf("%d) %s\n", gAccount[i].number, gAccount[i].fd->id);
  }
  puts("\nPlease enter the number of the account you want to delete");
  num = retNumber(2LL);
  if ( gStockCnt num && num <= 3 )
  {
    if puts("Please enter the code number of the candy to be purchased.");
    candyInfo[0] = retNumber(3LL)( gAccount[--num].state == 3 )
    {
      gAccount[num].state = 0LL;
    if ( candyInfogAccount[0num].fd->state < gStockCnt )
    {= 0LL;
      putsprintf("Please enter the number of the candy to purchase."The account(%s) has been deleted.\n", gAccount[num].fd->id);
      candyInfomemset(gAccount[1num].fd, = retNumber(3LL0, 0x80uLL);
      if free( gStock[candyInfo[0]]->candyNumber < candyInfo[1] )
      {
        if ( gStock[candyInfo[0]]->candyNumber < candyInfo[1] )gAccount[num].fd);
      gAccount[num].fd = (struct IDPW *)((char *)gAccount[num].fd - 16);
    }
    else
    {
      puts("ThereYou iscan not delete enoughthe stockaccount.");
    }
  }
  return __readfsqword(0x28u)   else if ( candyInfo[1] * gStock[candyInfo[0]]->candyPrice > gLoginAccount->bk )
      {
        printf(
          "You do not have enough money.(%ld)\n",
          candyInfo[1] * gStock[candyInfo[0]]->candyPrice,
          *(_QWORD *)candyInfo);
      }^ v4;
}

charge

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 사용자로 부터 충전 할 금액의 번호는 입력 받습니다.
    • 해당 함수는 해당 금액을 "gLoginAccount→bk" 영역에 더합니다.
Code Block
languagecpp
titlecharge()
unsigned __int64 charge()
{
  unsigned int chargeInfo[2]; // [rsp+0h] [rbp-10h]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  chargeInfo[0] = 0;
  puts("Please select the amount to charge.");
  puts("0) 1");
  puts("1) 10");
  puts("2) 100");
  puts("3) 1000");
  puts("4) 10000");
  puts("5) 100000");
  chargeInfo[1] = retNumber(2LL);
  switch ( chargeInfo[1] )
  {
    case 0u:
      chargeInfo[0] = 1;
      elsebreak;
    case  {1u:
        gStock[candyInfochargeInfo[0]]->candyNumber -= candyInfo[1]10;
      break;
  if ( !gStock[candyInfo[0]]->candyNumber ) case 2u:
      chargeInfo[0] = {100;
      break;
    case printf(3u:
      chargeInfo[0] = 1000;
    "Thank you for your purchase.(%ld)\n", break;
    case 4u:
       candyInfochargeInfo[10] * gStock[candyInfo[0]]->candyPrice,
= 10000;
      break;
    case 5u:
  *(_QWORD *)candyInfo);
   chargeInfo[0]       reSortStock(v0)= 100000;
          setBoard()break;
        }default:
      }break;
    }
  }
  elsegLoginAccount->bk += chargeInfo[0];
  {
    puts("We have not any candy.");
  }printf("%ld yen charged.\n", chargeInfo[0], *(_QWORD *)chargeInfo);
  return __readfsqword(0x28u) ^ v3v2;
}

...

changePW

  • 해당 함수는 다음과 같은 기능을 합니다.

    • 해당 함수는

      malloc() 함수를 이용해 1200 byte의 Heap 영역을 할당 받습니다

      전역 변수 "gAccount[i].state"를 이용해 비밀번호 변경이 가능한 계정을 출력합니다.

    • 해당 함수는

      해당 영역에

      사용자로 부터

      값을 입력 받아 저장합니다.

      비밀번호를 변경할 계정의 번호를 입력 받습니다.

    • 해당 함수는 해당 계정의 '.fd.state' 영역에 저장된 값이 '0'이 아닐 경우 비밀번호를 변경할 수 있습니다.

    • 해당 기능을 이용해 

Code Block
languagecpp
titlesetBoardchangePW()
unsigned __int64 setBoardchangePW()
{
  unsigned __int64int v0i; // ST08_8

  v0 = __readfsqword(0x28u);
  puts("Please enter a comment for candy.");
  board = (__int64)malloc(1200uLL [rsp+8h] [rbp-18h]
  unsigned int num; // [rsp+Ch] [rbp-14h] MAPDST
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  UserInput(board, 1200LLputs("\nAccount list");
  returnfor __readfsqword(0x28u) ^ v0;
}

charge

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 사용자로 부터 충전 할 금액의 번호는 입력 받습니다.
    • 해당 함수는 해당 금액을 "gLoginAccount→bk" 영역에 더합니다.
Code Block
languagecpp
titlecharge()
unsigned __int64 charge()
( i = 0; i <= 2; ++i )
  {
  unsigned int chargeInfo[2]; // [rsp+0h] [rbp-10h]if ( gAccount[i].state )
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u printf("%ld) %s\n", gAccount[i].number, gAccount[i].fd->id);
  chargeInfo[0] = 0;}
  puts("Please select the amount to charge.\nPlease enter the number of the account you want to change PW");
  num = puts("0) 1"retNumber(2LL);
  if puts("1) 10"); num )
  puts("2) 100");
  puts("3) 1000");
  puts("4) 10000");
  puts("5) 100000");
  chargeInfo[1] = retNumber(2LL);
  switch ( chargeInfo[1] )
{
    if ( num <= 3 )
    {
      if ( gAccount[--num].fd )
      {
    case 0u:
   if (  chargeInfogAccount[0] = 1;
num].fd->state )
        break;{
    case 1u:
      chargeInfo[0] = 10;
 puts("Enter your New Password.");
     break;
    case 2u:
      chargeInfo[0] = 100 UserInput(gAccount[num].fd->pw, 8LL);
      break;
    case 3u:}
      chargeInfo[0] = 1000;}
      break;}
    case 4u:}
  return __readfsqword(0x28u)   chargeInfo[0] = 10000;
      break;
    case 5u:
      chargeInfo[0] = 100000;
      break;
    default:
      break;
  }
  gLoginAccount->bk += chargeInfo[0];
  printf("%ld yen charged.\n", chargeInfo[0], *(_QWORD *)chargeInfo);
  return __readfsqword(0x28u) ^ v2;
}

.init_array

  • 다음과 같이 .init
Code Block
titlereadelf
lazenca0x0@ubuntu:~/Documents/CTF/SECCON2017$ gdb -q ./L*
Reading symbols from ./Lazenca.0x0...(no debugging symbols found)...done.
gdb-peda$ readelf 
.interp = 0x400238
.note.ABI-tag = 0x400254
.note.gnu.build-id = 0x400274
.gnu.hash = 0x400298
.dynsym = 0x4002b8
.dynstr = 0x4004c8
.gnu.version = 0x400580
.gnu.version_r = 0x4005b0
.rela.dyn = 0x4005e0
.rela.plt = 0x4005f8
.init = 0x4007d8
.plt = 0x400800
.plt.got = 0x400950
.text = 0x400960
.fini = 0x4029c4
.rodata = 0x4029d0
.eh_frame_hdr = 0x40376c
.eh_frame = 0x403888
.init_array = 0x603e08
.fini_array = 0x603e18
.jcr = 0x603e20
.dynamic = 0x603e28
.got = 0x603ff8
.got.plt = 0x604000
.data = 0x6040b8
.bss = 0x6040e0
gdb-peda$ x/2gx 0x603e08
0x603e08:	0x0000000000400a30	0x000000000040266b
gdb-peda$ x/5i 0x000000000040266b
   0x40266b:	push   rbp
   0x40266c:	mov    rbp,rsp
   0x40266f:	sub    rsp,0x10
   0x402673:	mov    rax,QWORD PTR fs:0x28
   0x40267c:	mov    QWORD PTR [rbp-0x8],rax
gdb-peda$ 

addAdmin

  • 해당 함수에서는 다음과 같은 Struct를 사용합니다.

...

titleStruct IDPW, ACCOUNT
^ v4;
}


printStock

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 전역 변수 gStock를 이용해 사탕 정보를 출력합니다.
Code Block
languagecpp
titleprintStock()
unsigned __int64 printStock()
{
  unsigned int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( gStockCnt )
  {
    for ( i = 0; i < gStockCnt; ++i )
    {
      printf("\n=*= Candy %d =*=\n", i);
      printf("Name of candy        : %s\n", gStock[i]);
      printf("Number of candy      : %d\n", gStock[i]->candyNumber);
      printf("Price of candy       : %d\n", gStock[i]->candyPrice);
      printf("Description of candy : %s\n", gStock[i]->candyDescription);
    }
  }
  else
  {
    puts("We have not any candy.");
  }
  return __readfsqword(0x28u) ^ v2;
}
  • 다음과 같은 구조체를 사용합니다.
Code Block
languagecpp
titlestruct STOCK
struct STOCK{
    char candyName[8];
    unsigned int  candyNumber;
    unsigned int  candyPrice;
    char *candyDescription;
};

purchase

  • 해당 함수는 다음과 같은 기능을 합니다.

    • 해당 함수는 전역 변수 gStockCnt의 값이 0일 경우 메시지 출력 해당 기능을 종료합니다.

    • 해당 함수는 전역 변수 gStockCnt의 값이 0이 아닐 경우 다음과 같은 기능을 합니다.

      • 해당 함수는 사용자로 부터 구매 할 캔디의 코드 번호(candyInfo[0]), 캔디의 수(candyInfo[1])를 입력받습니다.

        • 해당 함수는 구매 가능한 캔디 코드 번호, 캔디의 개수인지 확인합니다.

      • 해당 함수는 정상적인 구매가 진행 되며, 캔디의 재고가 없을 경우 아래 함수가 호출됩니다.

        • reSortStock()

        • setBoard()

Code Block
languagecpp
titlepurchase
  • 해당 함수는 다음과 같은 기능을 합니다.

    • 전역 변수 gAccount[0]에 다음과 같은 값을 저장 합니다.

      • state = 1

      • number = 1

      • fd→id = "Admin"

      • fd→pw = "admin"

    • state 변수에 1을 저장하고 있으며, 해당 계정으로 "orderMenu", "Account" 기능을 이용 할 수 있습니다.

Code Block
languagecpp
titleaddAdmin()
unsigned __int64 addAdminpurchase()
{
  unsigned __int64int v0; // ST08ST00_84

  v0unsigned = __readfsqword(0x28u);
  gAccount[0].state = 1LL;
  gAccount[0].number = 1LL;
  gAccount[0].fd = (struct IDPW *)malloc(0x80uLL);
  gAccount[0].bk = 880LL;
  strncpy(gAccount[0].fd->id, "Admin", 8uLL);
  strncpy(gAccount[0].fd->pw, "admin", 8uLL);
  strncpy(gAccount[0].fd->description, "I'm Lazenca.0x0\nWebsite is \"https://www.lazenca.net/\"", 0x58uLL);
  return __readfsqword(0x28u) ^ v0;
}

changePW

  • 해당 함수는 다음과 같은 기능을 합니다.

    • 해당 함수는 전역 변수 "gAccount[i].state"를 이용해 비밀번호 변경이 가능한 계정을 출력합니다.

    • 해당 함수는 사용자로 부터 비밀번호를 변경할 계정의 번호를 입력 받습니다.

    • 해당 함수는 해당 계정의 '.fd.state' 영역에 저장된 값이 '0'이 아닐 경우 비밀번호를 변경할 수 있습니다.

Code Block
languagecpp
titlechangePW()
unsigned __int64 changePW()
{
  unsigned int i; // [rsp+8h] [rbp-18h]
  unsigned int num; // [rsp+Ch] [rbp-14h] MAPDST
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts("\nAccount list");
  for ( i = 0; i <= 2; ++i )
  {
    if ( gAccount[i].state )
      printf("%ld) %s\n", gAccount[i].number, gAccount[i].fd->id);
  }
  puts("\nPlease enter the number of the account you want to change PW");
  num = retNumber(2LL);
  if ( num )
  {
    if ( num <= 3 )
int candyInfo[2]; // [rsp+0h] [rbp-10h]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( gStockCnt )
  {
    puts("Please enter the code number of the candy to be purchased.");
    candyInfo[0] = retNumber(3LL);
    if ( candyInfo[0] < gStockCnt )
    {
      puts("Please enter the number of the candy to purchase.");
      candyInfo[1] = retNumber(3LL);
      if ( gStock[candyInfo[0]]->candyNumber < candyInfo[1] )
      {
        if ( gStock[candyInfo[0]]->candyNumber < candyInfo[1] )
          puts("There is not enough stock.");
      }
      else if ( candyInfo[1] * gStock[candyInfo[0]]->candyPrice > gLoginAccount->bk )
      {
        printf(
          "You do not have enough money.(%ld)\n",
          candyInfo[1] * gStock[candyInfo[0]]->candyPrice,
          *(_QWORD *)candyInfo);
      }
      else
      {
      if ( gAccount[--num].fd )
      {
        if ( gAccount[num].fd->state )
        {
          puts("Enter your New Password.");
          UserInput(gAccount[num].fd->pw, 8LL);
        }
      }
    }
  }gStock[candyInfo[0]]->candyNumber -= candyInfo[1];
        if ( !gStock[candyInfo[0]]->candyNumber )
        {
          printf(
            "Thank you for your purchase.(%ld)\n",
            candyInfo[1] * gStock[candyInfo[0]]->candyPrice,
            *(_QWORD *)candyInfo);
          reSortStock(v0);
          setBoard();
        }
      }
    }
  }
  else
  {
    puts("We have not any candy.");
  }
  return __readfsqword(0x28u) ^ v3;
}

setBoard()

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 malloc() 함수를 이용해 1200 byte의 Heap 영역을 할당 받습니다.
    • 해당 함수는 해당 영역에 사용자로 부터 값을 입력 받아 저장합니다.
Code Block
languagecpp
titlesetBoard()
unsigned __int64 setBoard()
{
  unsigned __int64 v0; // ST08_8

  v0 = __readfsqword(0x28u);
  puts("Please enter a comment for candy.");
  board = (__int64)malloc(1200uLL);
  UserInput(board, 1200LL);
  return __readfsqword(0x28u) ^ v0;
}

.init_array

  • 다음과 같이 .init
Code Block
titlereadelf
lazenca0x0@ubuntu:~/Documents/CTF/SECCON2017$ gdb -q ./L*
Reading symbols from ./Lazenca.0x0...(no debugging symbols found)...done.
gdb-peda$ readelf 
.interp = 0x400238
.note.ABI-tag = 0x400254
.note.gnu.build-id = 0x400274
.gnu.hash = 0x400298
.dynsym = 0x4002b8
.dynstr = 0x4004c8
.gnu.version = 0x400580
.gnu.version_r = 0x4005b0
.rela.dyn = 0x4005e0
.rela.plt = 0x4005f8
.init = 0x4007d8
.plt = 0x400800
.plt.got = 0x400950
.text = 0x400960
.fini = 0x4029c4
.rodata = 0x4029d0
.eh_frame_hdr = 0x40376c
.eh_frame = 0x403888
.init_array = 0x603e08
.fini_array = 0x603e18
.jcr = 0x603e20
.dynamic = 0x603e28
.got = 0x603ff8
.got.plt = 0x604000
.data = 0x6040b8
.bss = 0x6040e0
gdb-peda$ x/2gx 0x603e08
0x603e08:	0x0000000000400a30	0x000000000040266b
gdb-peda$ x/5i 0x000000000040266b
   0x40266b:	push   rbp
   0x40266c:	mov    rbp,rsp
   0x40266f:	sub    rsp,0x10
   0x402673:	mov    rax,QWORD PTR fs:0x28
   0x40267c:	mov    QWORD PTR [rbp-0x8],rax
gdb-peda$ 

addAdmin

  • 해당 함수에서는 다음과 같은 Struct를 사용합니다.
Code Block
titleStruct IDPW, ACCOUNT
  • 해당 함수는 다음과 같은 기능을 합니다.

    • 전역 변수 gAccount[0]에 다음과 같은 값을 저장 합니다.

      • state = 1

      • number = 1

      • fd→id = "Admin"

      • fd→pw = "admin"

    • state 변수에 1을 저장하고 있으며, 해당 계정으로 "orderMenu", "Account" 기능을 이용 할 수 있습니다.

Code Block
languagecpp
titleaddAdmin()
unsigned __int64 addAdmin()
{
  unsigned __int64 v0; // ST08_8

  v0 = __readfsqword(0x28u);
  gAccount[0].state = 1LL;
  gAccount[0].number = 1LL;
  gAccount[0].fd = (struct IDPW *)malloc(0x80uLL);
  gAccount[0].bk = 880LL;
  strncpy(gAccount[0].fd->id, "Admin", 8uLL);
  strncpy(gAccount[0].fd->pw, "admin", 8uLL);
  strncpy(gAccount[0].fd->description, "I'm Lazenca.0x0\nWebsite is \"https://www.lazenca.net/\"", 0x58uLL);
  return __readfsqword(0x28u) ^ v4v0;
}



Code Block
unsigned __int64 orderList()
{
  unsigned int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( gOrderCnt )
  {
    puts("\n=*= Order list =*=");
    for ( i = 0; i < gOrderCnt; ++i )
    {
      printf("\nOrder code  : %s\n", gOrderList[i]);
      printf("Order count : %d\n", gOrderList[i]->orderNumber);
      printf("Order candy : %s\n", gOrderList[i]->orderCandyName);
      printf("Candy code  : %d\n", (unsigned int)gOrderList[i]->candyCode);
    }
  }
  else
  {
    puts("You have never ordered a product.");
  }
  return __readfsqword(0x28u) ^ v2;
}

...