Versions Compared

Key

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

...

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 전역 변수 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, UAF 취약성이 발생하게 됩니다.
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[num].fd, 0, 0x80uLL);
      free(gAccount[num].fd);
      gAccount[num].fd = (struct IDPW *)((char *)gAccount[num].fd - 16);
    }
    else
    {
      puts("You can not delete the account.");
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

Proof of concept

Fake chunk

changePW

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

    해당 프로그램에서 취약성을 이해하기 위해 ACCOUNT 구조체에 대한 이해가 필요합니다

    .

    • 해당

      구조체는 전역 변수로 선언되어 있습니다.
    • 해당 프로그램은 3개의 ACCOUNT 구조체를 사용합니다.
      • 첫번째 구조체에는 'Admin' 계정 정보가 저장되어 있습니다.
      • 2,3번째 구조체는 사용자가 생성한 계정의 정보가 저장됩니다.

...

  • House of lore 취약성은 다음과 같은 Fake chunk가 필요합니다.
    • delAccount() 함수를 이용해 gAccount[1].fd 영역에 Free chunk의 Head 주소를 저장 할 수 있습니다.

    • charge() 함수를 이용해 gAccount[1].bk, gAccount[2].bk 영역의 값을 변경 할 수 있습니다.

...

titleFake chunk

...

    • 함수는 전역 변수 "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 )
    {
      if ( gAccount[--num].fd )
      {
        if ( gAccount[num].fd->state )
        {
          puts("Enter your New Password.");
          UserInput(gAccount[num].fd->pw, 8LL);
        }
      }
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

orderMenu()

  • 해당 함수는 gLoginAccount->state의 값이 1인 경우 사용가능합니다.
  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 사용 가능한 기능 목록을 출력합니다.
      • 주문 목록, 주문 목록 추가, 주문 목록 취소, 캔디 주문
    • 해당 함수는 사용자로 부터 사용할 기능의 번호를 입력 받아 해당 기능을 호출합니다.
Code Block
unsigned __int64 orderMenu()
{
  signed int i; // [rsp+8h] [rbp-88h]
  unsigned int control; // [rsp+Ch] [rbp-84h]
  char funcList[5][22]; // [rsp+10h] [rbp-80h]
  unsigned __int64 v4; // [rsp+88h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  control = 1;
  strcpy((char *)funcList, "Order List");
  *(_DWORD *)&funcList[0][16] = 0;
  *(_WORD *)&funcList[0][20] = 0;
  strcpy(funcList[1], "Add to Order List");
  *(_WORD *)&funcList[1][20] = 0;
  *(_QWORD *)&funcList[2][0] = 'o lecnaC';
  *(_QWORD *)&funcList[2][8] = 'o s���en';
  *(_DWORD *)&funcList[2][16] = 'redr';
  *(_WORD *)&funcList[2][20] = '.';
  strcpy(funcList[3], "Order candy");
  *(_DWORD *)&funcList[3][16] = 0;
  *(_WORD *)&funcList[3][20] = 0;
  *(_OWORD *)&funcList[4][0] = (unsigned __int64)'tixE';
  *(_DWORD *)&funcList[4][16] = 0;
  *(_WORD *)&funcList[4][20] = 0;
LABEL_11:
  while ( control )
  {
    puts("\nOrder candy.");
    for ( i = 0; i <= 4; ++i )
      printf("%d) %s\n", (unsigned int)(i + 1), funcList[i]);
    printf("Command : ");
    switch ( (unsigned int)retNumber(2LL) )
    {
      case 1u:
        orderList();
        break;
      case 2u:
        addToOrderList();
        break;
      case 3u:
        orderCancel();
        break;
      case 4u:
        orderCandy();
        break;
      case 5u:
        control = 0;
        break;
      default:
        goto LABEL_11;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

addToOrderList

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

    • 해당 함수는 choiceCandy() 함수를 사용해 주문할 사탕의 번호를 입력받습니다.

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

      • 해당 함수는 해당 영역에 주문할 사탕의 정보를 저장합니다.

Code Block
languagecpp
titleaddToOrderList
unsigned __int64 addToOrderList()
{
  struct ORDER *newOrder; // ST08_8
  unsigned int tmp; // eax
  unsigned int candyNum; // [rsp+4h] [rbp-1Ch]
  char strOrdNum[8]; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( (unsigned int)gOrderCnt > 9 )
  {
    puts("You can not order candy anymore.");
  }
  else
  {
    candyNum = choiceCandy();
    if ( candyNum > 9 )
    {
      puts("Please enter a number between 0 and 9");
    }
    else
    {
      newOrder = (struct ORDER *)malloc(24uLL);
      tmp = getOrderNum();
      sprintf(strOrdNum, "%d", tmp);
      strncpy(newOrder->orderCode, strOrdNum, 1uLL);
      newOrder->orderNumber = gCandies[candyNum]->orderNumber;
      strncpy(newOrder->orderCandyName, gCandies[candyNum]->candyName, 8uLL);
      newOrder->candyCode = gCandies[candyNum]->candyCode;
      gOrderList[gOrderCnt++] = newOrder;
      orderList();
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

orderCancel

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

    • 해당 함수는 checkCancel(i) 함수를 취소 가능한 주문을 확인하고, 주문 취소 가능한 목록을 출력합니다.

    • 해당 함수는 취소 가능한 주문이 있으며, 사용자로 부터 주문 번호을 입력받습니다.

    • 해당 함수는 입력 값을 검증한 후 reSort() 함수를 호출 해 주문을 취소합니다.
Code Block
unsigned __int64 orderCancel()
{
  unsigned int i; // [rsp+Ch] [rbp-44h]
  int j; // [rsp+Ch] [rbp-44h]
  int orderCnt; // [rsp+10h] [rbp-40h]
  unsigned int canNum; // [rsp+1Ch] [rbp-34h]
  int cancelableList[10]; // [rsp+20h] [rbp-30h]
  unsigned __int64 v6; // [rsp+48h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  orderCnt = 0;
  if ( gOrderCnt )
  {
    for ( i = 0; i < gOrderCnt; ++i )
    {
      if ( (unsigned int)checkCancel(i) )
      {
        cancelableList[orderCnt++] = i;
        printf("\n=*= Cancelable order (Order number : %d) =*=\n", i);
        printf("Order 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);
      }
    }
    if ( orderCnt )
    {
      canNum = retNumber(2LL);
      for ( j = 0; j < orderCnt; ++j )
      {
        if ( cancelableList[j] == canNum )
          reSort(canNum);
      }
    }
  }
  else
  {
    puts("You have never ordered a product.");
  }
  return __readfsqword(0x28u) ^ v6;
}

reSort

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 free() 함수를 이용해 gOrderList[a1] 영역을 해제합니다.

Code Block
languagecpp
titlereSort(unsigned int a1)
unsigned __int64 __fastcall reSort(unsigned int a1)
{
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  free(gOrderList[a1]);
  if ( a1 < gOrderCnt )
  {
    for ( i = 0; a1 + i < gOrderCnt; ++i )
      gOrderList[a1 + i] = gOrderList[a1 + i + 1];
  }
  gOrderList[gOrderCnt--] = 0LL;
  return __readfsqword(0x28u) ^ v3;
}

orderCandy()

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

    • 해당 함수는 사용자로 부터 캔디 주문의 진행 여부를 확인합니다.

    • 해당 함수는 getStockNum() 함수를 사용해 gOrderList[]에 저장된 값이 gStock[]에 존재하는지 확인합니다.

      • 해당 함수는 gOrderList[]에 저장된 값 gStock[]에 존재 할 경우 다음과 같이 처리됩니다.

        • "gStock[]->candyNumber" 에 "gOrderList[]->orderNumber"의 값을 더하게 됩니다.

      • 해당 함수는 gOrderList[]에 저장된 값 gStock[]에 존재하지 않을 경우 다음과 같이 처리됩니다.

        • 해당 함수는 malloc() 함수를 사용하여 24 byte의 heap 영역을 할당합니다.

        • 해당 함수는 해당 영역에 캔디에 대한 정보를 저장합니다.

          • 사탕 이름, 가격, 캔디 정보가 저장되어 있는 주소 값

        • 그리고 해당 함수는 124 byte의 heap 영역을 할당해서, 해당 영역에 사용자로 부터 입력받은 캔디 정보를 저장합니다.

    • 해당 함수는 gOrderList[]에 저장된 값 gStock[]에 모두 저장한 후, gOrderList[] 영역을 모두 해제 합니다.

Code Block
unsigned __int64 orderCandy()
{
  struct STOCK *dest; // ST10_8
  unsigned int i; // [rsp+4h] [rbp-1Ch]
  int num; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v4

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 )
    {
      if ( gAccount[--num].fd )
      {
        if ( gAccount[num].fd->state )
        {
          puts("Enter your New Password.");
          UserInput(gAccount[num].fd->pw, 8LL);
        }
      }
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

orderMenu()

  • 해당 함수는 gLoginAccount->state의 값이 1인 경우 사용가능합니다.
  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 사용 가능한 기능 목록을 출력합니다.
      • 주문 목록, 주문 목록 추가, 주문 목록 취소, 캔디 주문
    • 해당 함수는 사용자로 부터 사용할 기능의 번호를 입력 받아 해당 기능을 호출합니다.
Code Block
unsigned __int64 orderMenu()
{
  signed int i; // [rsp+8h] [rbp-88h]
  unsigned int control; // [rsp+Ch] [rbp-84h]
  char funcList[5][22]; // [rsp+10h] [rbp-80h]
  unsigned __int64 v4; // [rsp+88h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  control = 1;
  strcpy((char *)funcList, "Order List");
  *(_DWORD *)&funcList[0][16] = 0;
  *(_WORD *)&funcList[0][20] = 0;
  strcpy(funcList[1], "Add to Order List");
  *(_WORD *)&funcList[1][20] = 0;
  *(_QWORD *)&funcList[2][0] = 'o lecnaC';
  *(_QWORD *)&funcList[2][8] = 'o s���en';
  *(_DWORD *)&funcList[2][16] = 'redr';
  *(_WORD *)&funcList[2][20] = '.';
  strcpy(funcList[3], "Order candy");
  *(_DWORD *)&funcList[3][16] = 0;
  *(_WORD *)&funcList[3][20] = 0;
  *(_OWORD *)&funcList[4][0] = (unsigned __int64)'tixE';
  *(_DWORD *)&funcList[4][16] = 0;
  *(_WORD *)&funcList[4][20] = 0;
LABEL_11:
  while ( control )
  {
    puts("\nOrder candy.");
    for ( i = 0; i <= 4; ++i )
      printf("%d) %s\n", (unsigned int)(i + 1), funcList[i]);
    printf("Command : ");
    switch ( (unsigned int)retNumber(2LL) )
    {
      case 1u:
        orderList();
        break;
      case 2u:
        addToOrderList();
        break;
      case 3u:
        orderCancel();
        break;
      case 4u:
        orderCandy();
        break;
      case 5u:
        control = 0;
        break;
      default:
        goto LABEL_11;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

addToOrderList

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

    • 해당 함수는 choiceCandy() 함수를 사용해 주문할 사탕의 번호를 입력받습니다.

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

      • 해당 함수는 해당 영역에 주문할 사탕의 정보를 저장합니다.

Code Block
languagecpp
titleaddToOrderList
unsigned __int64 addToOrderList()
{
  struct ORDER *newOrder; // ST08_8
  unsigned int tmp; // eax
  unsigned int candyNum; // [rsp+4h] [rbp-1Ch]
  char strOrdNum[8]; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( (unsigned int)gOrderCnt > 9 )
  {
    puts("You can not order candy anymore.");
  }
  else
  {
    candyNum = choiceCandy();
    if ( candyNum > 9 )
    {
      puts("Please enter a number between 0 and 9");
    }
    else
    {
      newOrder = (struct ORDER *)malloc(24uLL);
      tmp = getOrderNum();
      sprintf(strOrdNum, "%d", tmp);
      strncpy(newOrder->orderCode, strOrdNum, 1uLL);
      newOrder->orderNumber = gCandies[candyNum]->orderNumber;
      strncpy(newOrder->orderCandyName, gCandies[candyNum]->candyName, 8uLL);
      newOrder->candyCode = gCandies[candyNum]->candyCode;
      gOrderList[gOrderCnt++] = newOrder;
      orderList();
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

orderCancel

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

    • 해당 함수는 checkCancel(i) 함수를 취소 가능한 주문을 확인하고, 주문 취소 가능한 목록을 출력합니다.

    • 해당 함수는 취소 가능한 주문이 있으며, 사용자로 부터 주문 번호을 입력받습니다.

    • 해당 함수는 입력 값을 검증한 후 reSort() 함수를 호출 해 주문을 취소합니다.
Code Block
unsigned __int64 orderCancel()
{
  unsigned int i; // [rsp+Ch] [rbp-44h]
  int j; // [rsp+Ch] [rbp-44h]
  int orderCnt; // [rsp+10h] [rbp-40h]
  unsigned int canNum; // [rsp+1Ch] [rbp-34h]
  int cancelableList[10]; // [rsp+20h] [rbp-30h]
  unsigned __int64 v6; // [rsp+48h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  orderCnt = 0;
  if ( gOrderCnt )
  {
    for ( i = 0; i < gOrderCnt; ++i )
    {
      if ( (unsigned int)checkCancel(i) )
      {
        cancelableList[orderCnt++] = i;
        printf("\n=*= Cancelable order (Order number : %d) =*=\n", i);
        printf("Order 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);
      }
    }
    if ( orderCnt )
    {
      canNum = retNumber(2LL);
      for ( j = 0; j < orderCnt; ++j )
      {
        if ( cancelableList[j] == canNum )
          reSort(canNum);
      }
    }
  }
  else
  {
    puts("You have never ordered a product.");
  }
  return __readfsqword(0x28u) ^ v6;
}

reSort

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당 함수는 free() 함수를 이용해 gOrderList[a1] 영역을 해제합니다.

Code Block
languagecpp
titlereSort(unsigned int a1)
unsigned __int64 __fastcall reSort(unsigned int a1)
{
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3v4 = __readfsqword(0x28u);
  free(gOrderList[a1]);
  if ( a1 < gOrderCnt )
  {
    for orderList();
 i = 0; a1 + i < gOrderCnt; ++i ) puts("\nWould you like to order these candies?");
    puts("0)  gOrderList[a1 + i] = gOrderList[a1 + i + 1];
  }
  gOrderList[gOrderCnt--] = 0LL;
  return __readfsqword(0x28u) ^ v3;
}

orderCandy()

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

    • 해당 함수는 사용자로 부터 캔디 주문의 진행 여부를 확인합니다.

    • 해당 함수는 getStockNum() 함수를 사용해 gOrderList[]에 저장된 값이 gStock[]에 존재하는지 확인합니다.

      • 해당 함수는 gOrderList[]에 저장된 값 gStock[]에 존재 할 경우 다음과 같이 처리됩니다.

        • "gStock[]->candyNumber" 에 "gOrderList[]->orderNumber"의 값을 더하게 됩니다.

      • 해당 함수는 gOrderList[]에 저장된 값 gStock[]에 존재하지 않을 경우 다음과 같이 처리됩니다.

        • 해당 함수는 malloc() 함수를 사용하여 24 byte의 heap 영역을 할당합니다.

        • 해당 함수는 해당 영역에 캔디에 대한 정보를 저장합니다.

          • 사탕 이름, 가격, 캔디 정보가 저장되어 있는 주소 값

        • 그리고 해당 함수는 124 byte의 heap 영역을 할당해서, 해당 영역에 사용자로 부터 입력받은 캔디 정보를 저장합니다.

    • 해당 함수는 gOrderList[]에 저장된 값 gStock[]에 모두 저장한 후, gOrderList[] 영역을 모두 해제 합니다.

Code Block
unsigned __int64 orderCandy()
{
  struct STOCK *dest; // ST10_8
  unsigned int i; // [rsp+4h] [rbp-1Ch]
  int num; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  if ( gOrderCnt )
  {
    orderList();
    puts("\nWould you like to order these candies?");
    puts("0) Yes, 1) No");
    if ( !(unsigned int)retNumber(2LL) )
    {
      for ( i = 0; i < gOrderCnt; ++i )
      {
        num = getStockNum(i)Yes, 1) No");
    if ( !(unsigned int)retNumber(2LL) )
    {
      for ( i = 0; i < gOrderCnt; ++i )
      {
        num = getStockNum(i);
        if ( num )
        {
          gStock[num - 1]->candyNumber += gOrderList[i]->orderNumber;
        }
        else if ( (unsigned int)gStockCnt > 4 )
        {
          puts("The warehouse is full. Your new order can not be completed.");
        }
        else
        {
          puts("\nEnter information about newly added candy.");
          dest = (struct STOCK *)malloc(24uLL);
          strncpy(dest->candyName, gOrderList[i]->orderCandyName, 8uLL);
          dest->candyNumber = gOrderList[i]->orderNumber;
          printf("Enter the price of %s candy.\n", dest);
          dest->candyPrice = retNumber(5LL);
          printf("Enter a description of the %s candy.\n", dest);
          dest->candyDescription = (char *)malloc(124uLL);
        if  UserInput( num )dest->candyDescription, 124LL);
        {
          gStock[num - 1]->candyNumber += gOrderList[i]->orderNumbergStock[gStockCnt++] = dest;
        }
      }
  else if ( (unsigned int)gStockCntwhile >( 4gOrderCnt )
      {
  {
      free(gOrderList[gOrderCnt - 1]);
  puts("The warehouse is full. Your new order can not be completed.")gOrderList[gOrderCnt-- - 1] = 0LL;
        }
    }
  }
  else
        {
          puts("\nEnterYou informationhave aboutnever newlyordered addeda candyproduct.");
  }
  return __readfsqword(0x28u)     dest = (struct STOCK *)malloc(24uLL);
          strncpy(dest->candyName, gOrderList[i]->orderCandyName, 8uLL);
          dest->candyNumber = gOrderList[i]->orderNumber;
          printf("Enter the price of %s candy.\n", dest);
          dest->candyPrice = retNumber(5LL);
          printf("Enter a description of the %s candy.\n", dest);
          dest->candyDescription = (char *)malloc(124uLL);
          UserInput(dest->candyDescription, 124LL);
          gStock[gStockCnt++] = dest;
        }
      }
      while ( gOrderCnt )
      {
        free(gOrderList[gOrderCnt - 1]);
        gOrderList[gOrderCnt-- - 1] = 0LL;
      }
    }
  }
  else
  {
    puts("You have never ordered a product.");
  }
  return __readfsqword(0x28u) ^ v4;
}^ v4;
}

Proof of concept

Fake chunk

  • 해당 프로그램에서 취약성을 이해하기 위해 ACCOUNT 구조체에 대한 이해가 필요합니다.
    • 해당 구조체는 전역 변수로 선언되어 있습니다.
    • 해당 프로그램은 3개의 ACCOUNT 구조체를 사용합니다.
      • 첫번째 구조체에는 'Admin' 계정 정보가 저장되어 있습니다.
      • 2,3번째 구조체는 사용자가 생성한 계정의 정보가 저장됩니다.
Panel
0x604220Admin.stateAdmin.number
0x604230Admin.fdAdmin.bk
0x604240gAccount[1].stategAccount[1].number
0x604250gAccount[1].fdgAccount[1].bk
0x604260gAccount[2].stategAccount[2].number
0x604270gAccount[2].fdgAccount[2].bk
  • House of lore 취약성은 다음과 같은 Fake chunk가 필요합니다.
    • delAccount() 함수를 이용해 gAccount[1].fd 영역에 Free chunk의 Head 주소를 저장 할 수 있습니다.

    • charge() 함수를 이용해 gAccount[1].bk, gAccount[2].bk 영역의 값을 변경 할 수 있습니다.

Panel
titleFake chunk
0x604220Admin.stateAdmin.number
0x604230Admin.fdAdmin.bk
0x604240gAccount[1].stategAccount[1].number
0x604250gAccount[1].fd = Free chunk head - 0x16gAccount[1].bk = 0x604268
0x604260gAccount[2].stategAccount[2].number
0x604270gAccount[2].fdgAccount[2].bk = 0x604240

Overwrite Fd of Fack chunk

  • House of lore 취약성은 다음과 같은 Free chunk의 fd영역을 변경할 수 있어야 합니다.
    • delAccount() 함수에 의해 gAccount[1].fd 영역의 값이 변경됩니다.
      • gAccount[1].fd→id : fd 영역
      • gAccount[1].fd→pw : bk 영역
    • 해당 계정의 비밀번호 변경을 할 수 있으면 Free chunk의 bk영역에 값을 덮어 쓸수 있습니다.

  • 하지만 패스워드를 변경하기 위해서는 "gAccount[1].fd→state"의 값이 '0'이 아니어야 합니다.

Panel
titlegAccount[1].fd

Create an account

Delete account

gAccount[1].fd

0x8

0x0

0x0

prev_size

Size of chunk

Chunk (long empty[0])

Chunk size (long empty[0])

0x8

long empty[0]

long empty[1]

fd (char id[8])

bk (char pw[8])

0x10

char id[8]

char pw[8]

long state

char description[88]

0x18

long state

char description[88]



  • 다음과 같이 UAF 취약성을 사용해 "gAccount[1].fd→state" 값을 변경할 수 있습니다.

    • ACCOUNT 구조체의 크기와 

    • "gAccount[1].fd" 영역에 저장된 

Structure of Exploit code 

...