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이 실패시 사용자에게 계정을 생성 할 것인지 묻습니다.
- 해당는 함수는 사용자가 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" 기능을 이용 할 수 있습니다.
- 전역 변수 gAccount[0]에 다음과 같은 값을 저장 합니다.
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