You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

Excuse the ads! We need some help to keep our site up.

List


Infomation

Description

Lazenca.0x0

I have opened an online candy store.

File


Source Code


Writeup

File information

lazenca0x0@ubuntu:~/Documents/CTF/SECCON2017$ file ./Lazenca.0x0 
./Lazenca.0x0: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1bfd795acede916210985e5865d2de9697e7505a, stripped
lazenca0x0@ubuntu:~/Documents/CTF/SECCON2017$ checksec.sh --file ./Lazenca.0x0 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   ./Lazenca.0x0
lazenca0x0@ubuntu:~/Documents/CTF/SECCON2017$ 

Binary analysis

Struct

typedef struct ORDER{
    char orderCode[8];
    unsigned int  orderNumber;
    char orderCandyName[8];
    int  candyCode;
};

typedef struct CANDIES {
    char candyName[8];
    unsigned int  orderNumber;
    int candyCode;
};

typedef struct STOCK{
    char candyName[8];
    unsigned int  candyNumber;
    int  candyPrice;
    char *candyDescription;
};

Main

  • 해당 함수는 다음과 같은 기능을 합니다.
    • 해당는 함수는 setCandy() 함수를 호출하여 Candy 정보를 설정합니다.
    • 해당는 함수는 login() 함수를 호출하여 사용하여 계정정보를 확인합니다.
    • 해당는 함수는 사용자가 login에 실패 할 경우 다음과 같이 동작합니다.
      • 해당 함수는 login이 실패시 사용자에게 계정을 생성 할 것인지 묻습니다.
        • 해당 함수는 addAccount() 함수를 이용해 새로운 계정을 생성합니다.

      • 그리고 해당 함수는 login을 3번 실패하면 프로그램은 종료됩니다.
    • 해당는 함수는 사용자가 login에 성공하면 아래 기능들을 이용할 수 있습니다.
      • 제고 출력, 주문, 충전, 로그아웃
      • gLoginAccount→state 의 값이 1인 경우 "orderMenu", "Account" 기능을 이용 할 수 있습니다.
main
__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;
}




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", (unsigned int)gStock[i]->candyPrice);
      printf("Description of candy : %s\n", gStock[i]->candyDescription);
    }
  }
  else
  {
    puts("We have not any candy.");
  }
  return __readfsqword(0x28u) ^ v2;
}


purchase


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);
 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
 {
 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;
}


.init_array

  • 다음과 같이 .init
readelf
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를 사용합니다.


Struct IDPW, ACCOUNT
struct IDPW{
    long empty[2];
    char id[IDPWMAX];
    char pw[IDPWMAX];
    long state;
    char description[88];
};

struct ACCOUNT{
    long state;
    long number;
    struct IDPW *fd;
    long bk;
};


  • 해당 함수는 다음과 같은 기능을 합니다.
    • 전역 변수 gAccount[0]에 다음과 같은 값을 저장 합니다.
      • state = 1
      • number = 1
      • fd→id = "Admin"
      • fd→pw = "admin"
    • state 변수에 1을 저장하고 있으며, 해당 계정으로 "orderMenu", "Account" 기능을 이용 할 수 있습니다.


addAdmin
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) ^ v0;
}

Account


  • 해당 함수는 다음과 같은 기능을 합니다.
unsigned __int64 Account()
{
  int tmp; // eax
  signed int i; // [rsp+8h] [rbp-58h]
  signed int control; // [rsp+Ch] [rbp-54h]
  char funcList[3][22]; // [rsp+10h] [rbp-50h]
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  control = 1;
  strcpy((char *)funcList, "Delete account");
  *(_DWORD *)&funcList[0][16] = 0;
  *(_WORD *)&funcList[0][20] = 0;
  strcpy(funcList[1], "Change password");
  *(_DWORD *)&funcList[1][16] = 0;
  *(_WORD *)&funcList[1][20] = 0;
  *(_OWORD *)&funcList[2][0] = (unsigned __int64)'tixE';
  *(_DWORD *)&funcList[2][16] = 0;
  *(_WORD *)&funcList[2][20] = 0;
  while ( control )
  {
    puts("\nAccount.");
    for ( i = 0; i <= 2; ++i )
      printf("%d) %s\n", (unsigned int)(i + 1), funcList[i]);
    printf("Command : ");
    tmp = retNumber(2LL);
    switch ( tmp )
    {
      case 2:
        changePW();
        break;
      case 3:
        control = 0;
        break;
      case 1:
        delAccount();
        break;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}


delAccount



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;
}





Structure of Exploit code 


  • The following information is required for an attack:


Information for attack


Exploit Code

Exploit code

Flag

Flag


Related Site

  • N / a

  • No labels