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; }
charge
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; break; case 1u: chargeInfo[0] = 10; break; case 2u: chargeInfo[0] = 100; break; case 3u: chargeInfo[0] = 1000; break; case 4u: 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
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; }
addAccount
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; gAccount[i].state = a1; gAccount[i].number = i + 1; gAccount[i].fd = (struct IDPW *)malloc(0x80uLL); gAccount[i].fd->state = 1LL; puts("\nEnter your New ID."); UserInput(gAccount[i].fd->id, 8LL); puts("Enter your New Password."); 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; }
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; }
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; }
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; }
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; }
addToOrderList
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(0x18uLL); 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; }
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; }
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); 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(0x18uLL); 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(0x7CuLL); 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; }
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