...
login()
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 사용자로 부터 ID,Password를 입력 받습니다.
- 해당 함수는 입력 받은 값을 전역 변수 gAccount[]에 존재하는지 확인합니다.
- 해당 함수는 인증에 성공하면, 해당 계정 정보가 저장된 gAccount[]의 주소를 전역변수 "gLoginAccount" 에 저장합니다.
Code Block |
---|
signed __int64 login() { size_t lenUserInputID; // rbx size_t lenID; // rax size_t lenUserInputPW; // rbx size_t lenPW; // rax signed int i; // [rsp+Ch] [rbp-34h] char id[8]; // [rsp+10h] [rbp-30h] char pw[8]; // [rsp+20h] [rbp-20h] unsigned __int64 v8; // [rsp+28h] [rbp-18h] v8 = __readfsqword(0x28u); memset(id, 0, 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; ++i ) { if ( gAccount[i].state ) { lenUserInputID = strlen(id); if ( lenUserInputID == strlen(gAccount[i].fd->id) ) { lenID = strlen(gAccount[i].fd->id); if ( !strncmp(gAccount[i].fd->id, id, lenID) ) { lenUserInputPW = strlen(pw); if ( lenUserInputPW == strlen(gAccount[i].fd->pw) ) { lenPW = strlen(gAccount[i].fd->pw); if ( !strncmp(gAccount[i].fd->pw, pw, lenPW) ) { gLoginAccount = (struct ACCOUNT *)(32LL * i + 0x604220); printf("\nHi, %s", gAccount[i].fd->id); return 1LL; } } } } } } return 0LL; } |
...
- 해당 함수는 다음과 같은 기능을 합니다.
해당 함수는 전역 변수 gAccount[].state 의 값이 '0' 인 경우 다음과 같이 동작합니다.
해당 함수는 malloc()을 사용하여 128 byte의 heap 영역을 할당받습니다.
해당 함수는 해당 영역의 주소를 gAccount[i].fd에 저장합니다.
- 해당 함수는 해당 영역에 ID, Password,등의 정보를 저장합니다.
Code Block |
---|
struct IDPW{
long empty[2];
char id[8];
char pw[8];
long state;
char description[88];
};
struct ACCOUNT{
long state;
long number;
struct IDPW *fd;
long bk;
}; |
addAccount()
해당 함수는 다음과 같은 기능을 합니다.
해당 함수는 전역 변수 gAccount[].state 의 값이 '0' 인 경우 다음과 같이 동작합니다.
해당 함수는 malloc()을 사용하여 128 byte의 heap 영역을 할당받습니다.
해당 함수는 해당 영역의 주소를 gAccount[i].fd에 저장합니다.
- 해당 함수는 해당 영역에 ID, Password,등의 정보를 저장합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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 | ||||
| ||||
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(128uLL); gAccount[i].fd->state = 1LL; puts("\nEnter your New ID."); UserInput(gAccount[i].fd->id, 8LL)state = a1; puts("Enter your New Password.")gAccount[i].number = i + 1; UserInput(gAccount[i].fd->pw, 8LL = (struct IDPW *)malloc(128uLL); puts("Enter your profilegAccount[i].fd->state = 1LL; puts("\nEnter your New ID."); UserInput(gAccount[i].fd->description>id, 88LL8LL); gAccount[i].bk = 10000LLputs("Enter your New Password."); } } if ( empty ) UserInput(gAccount[i].fd->pw, 8LL); puts("CouldEnter notyour add userprofile."); return __readfsqword(0x28u) ^ v4; } |
- 해당 함수는 다음과 같은 구조체를 사용합니다.
Code Block |
---|
struct IDPW{ UserInput(gAccount[i].fd->description, 88LL); long emptygAccount[2i]; .bk char id[8]= 10000LL; char pw[8];} long state;} if ( char description[88]; }; struct ACCOUNT{ long stateempty ) puts("Could not add user."); return long number; struct IDPW *fd; long bk; }; |
...
__readfsqword(0x28u) ^ v4;
} |
purchase
해당 함수는 다음과 같은 기능을 합니다.
해당 함수는 전역
변수 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 취약성이 발생하게 됩니다.
변수 gStockCnt의 값이 0일 경우 메시지 출력 해당 기능을 종료합니다.
해당 함수는 전역 변수 gStockCnt의 값이 0이 아닐 경우 다음과 같은 기능을 합니다.
해당 함수는 사용자로 부터 구매 할 캔디의 코드 번호(candyInfo[0]), 캔디의 수(candyInfo[1])를 입력받습니다.
해당 함수는 구매 가능한 캔디 코드 번호, 캔디의 개수인지 확인합니다.
해당 함수는 정상적인 구매가 진행 되며, 캔디의 재고가 없을 경우 아래 함수가 호출됩니다.
reSortStock()
setBoard()
Code Block | ||||
---|---|---|---|---|
| ||||
Code Block | ||||
| ||||
unsigned __int64 delAccountpurchase() { unsigned int iv0; // [rsp+8h] [rbp-18h]ST00_4 unsigned int numcandyInfo[2]; // [rsp+Ch0h] [rbp-14h10h] MAPDST unsigned __int64 v4v3; // [rsp+18h8h] [rbp-8h] v4v3 = __readfsqword(0x28u); if puts("\nAccount list"); gStockCnt ) for{ ( i = 0; i <= 2; ++i ) { if ( gAccount[i].state ) puts("Please enter the code number of the candy to be purchased."); candyInfo[0] = retNumber(3LL); if printf("%d) %s\n", gAccount[i].number, gAccount[i].fd->id); } puts("\nPlease candyInfo[0] < gStockCnt ) { puts("Please enter the number of the account you wantcandy to deletepurchase."); num candyInfo[1] = retNumber(2LL3LL); if ( num && num <=if 3 ) ( gStock[candyInfo[0]]->candyNumber < candyInfo[1] ) { if ( gAccount[--num].state == 3gStock[candyInfo[0]]->candyNumber < candyInfo[1] ) { gAccount[num].state = 0LLputs("There is not enough stock."); gAccount[num].fd->state = 0LL; } else if printf("The account(%s) has been deleted.\n", gAccount[num].fd->id); candyInfo[1] * gStock[candyInfo[0]]->candyPrice > gLoginAccount->bk ) memset(gAccount[num].fd, 0, 0x80uLL); { free(gAccount[num].fd); printf( gAccount[num].fd = (struct IDPW *)((char *)gAccount[num].fd - 16); "You do not have enough money.(%ld)\n", } else candyInfo[1] {* gStock[candyInfo[0]]->candyPrice, puts("You can not delete the account."*(_QWORD *)candyInfo); } } else { return __readfsqword(0x28u) ^ v4; } |
charge
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 사용자로 부터 충전 할 금액의 번호는 입력 받습니다.
- 해당 함수는 해당 금액을 "gLoginAccount→bk" 영역에 더합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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;
} |
changePW
해당 함수는 다음과 같은 기능을 합니다.
해당 함수는 전역 변수 "gAccount[i].state"를 이용해 비밀번호 변경이 가능한 계정을 출력합니다.
해당 함수는 사용자로 부터 비밀번호를 변경할 계정의 번호를 입력 받습니다.
해당 함수는 해당 계정의 '.fd.state' 영역에 저장된 값이 '0'이 아닐 경우 비밀번호를 변경할 수 있습니다.
해당 기능을 이용해
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;
} |
- 다음과 같은 구조체를 사용합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
struct STOCK{
char candyName[8];
unsigned int candyNumber;
unsigned int candyPrice;
char *candyDescription;
}; |
setBoard()
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 malloc() 함수를 이용해 1200 byte의 Heap 영역을 할당 받습니다.
- 해당 함수는 해당 영역에 사용자로 부터 값을 입력 받아 저장합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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;
} |
charge
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 사용자로 부터 충전 할 금액의 번호는 입력 받습니다.
- 해당 함수는 해당 금액을 "gLoginAccount→bk" 영역에 더합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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); | ||||
Code Block | ||||
| ||||
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) ^ v4v2; } |
...
Account()
- 해당 함수는 gLoginAccount->state의 값이 1인 경우 사용가능합니다.
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 전역 변수 gStock를 이용해 사탕 정보를 사용 가능한 기능 목록을 출력합니다.
- 계정 삭제, 비밀번호 변경
- 해당 함수는 사용자로 부터 사용할 기능의 번호를 입력 받아 해당 기능을 호출합니다.
- 해당 함수는 전역 변수 gStock를 이용해 사탕 정보를 사용 가능한 기능 목록을 출력합니다.
Code Block | |
---|---|
Code Block | |
language | cpp | title | printStock()
unsigned __int64 printStockAccount() { int tmp; // eax unsignedsigned int i; // [rsp+4h8h] [rbp-58h] signed int control; // [rsp+Ch] [rbp-54h] char funcList[3][22]; // [rsp+10h] [rbp-50h] unsigned __int64 v2v5; // [rsp+8h58h] [rbp-8h] v2v5 = __readfsqword(0x28u); ifcontrol ( gStockCnt )= 1; { 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);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("Price of candy : %d\n", gStock[i]->candyPrice); %d) %s\n", (unsigned int)(i + 1), funcList[i]); printf("DescriptionCommand of candy : %s\n", gStock[i]->candyDescription); tmp = }retNumber(2LL); } switch else ( tmp {) puts("We have not any candy."{ case 2: changePW(); } return __readfsqword(0x28u) ^ v2break; } |
- 다음과 같은 구조체를 사용합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
struct STOCK{ char candyName[8];case 3: unsigned intcontrol = candyNumber0; unsigned int candyPricebreak; char *candyDescription; }; |
...
case 1:
delAccount();
break;
}
}
return __readfsqword(0x28u) ^ v5;
} |
delAccount()
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 전역 변수 gStockCnt의 값이 0일 경우 메시지 출력 해당 기능을 종료합니다.
해당 함수는 전역 변수 gStockCnt의 값이 0이 아닐 경우 다음과 같은 기능을 합니다.
해당 함수는 사용자로 부터 구매 할 캔디의 코드 번호(candyInfo[0]), 캔디의 수(candyInfo[1])를 입력받습니다.
해당 함수는 구매 가능한 캔디 코드 번호, 캔디의 개수인지 확인합니다.
해당 함수는 정상적인 구매가 진행 되며, 캔디의 재고가 없을 경우 아래 함수가 호출됩니다.
reSortStock()
setBoard()
- 변수 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 | ||||
---|---|---|---|---|
| ||||
Code Block | ||||
| ||||
unsigned __int64 purchasedelAccount() { unsigned int v0i; // ST00_4[rsp+8h] [rbp-18h] unsigned int candyInfo[2]num; // [rsp+0hCh] [rbp-10h14h] MAPDST unsigned __int64 v3v4; // [rsp+8h18h] [rbp-8h] v3v4 = __readfsqword(0x28u); if puts( gStockCnt )"\nAccount list"); { for ( i puts("Please enter the code number of the candy to be purchased."); candyInfo[0] = retNumber(3LL);= 0; i <= 2; ++i ) { if ( gAccount[i].state ) if printf( candyInfo[0] < gStockCnt ) { "%d) %s\n", gAccount[i].number, gAccount[i].fd->id); } puts("Please\nPlease enter the number of the account you candywant to purchase.delete"); candyInfo[1] num = retNumber(3LL2LL); if ( num if ( gStock[candyInfo[0]]->candyNumber&& num <= candyInfo[1]3 ) { if ( gStock[candyInfo[0]]->candyNumber < candyInfo[1]gAccount[--num].state == 3 ) { puts("There is not enough stock.")gAccount[num].state = 0LL; } gAccount[num].fd->state = 0LL; else if printf( candyInfo[1] * gStock[candyInfo[0]]->candyPrice > gLoginAccount->bk )"The account(%s) has been deleted.\n", gAccount[num].fd->id); { memset(gAccount[num].fd, 0, 0x80uLL); printf(free(gAccount[num].fd); gAccount[num].fd = (struct "You do not have enough money.(%ld)\n",IDPW *)((char *)gAccount[num].fd - 16); } else candyInfo[1] * gStock[candyInfo[0]]->candyPrice, { puts("You can not delete *(_QWORD *)candyInfo)the account."); } else} return __readfsqword(0x28u) ^ { 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(); }v4; } |
changePW
해당 함수는 다음과 같은 기능을 합니다.
해당 함수는 전역 변수 "gAccount[i].state"를 이용해 비밀번호 변경이 가능한 계정을 출력합니다.
해당 함수는 사용자로 부터 비밀번호를 변경할 계정의 번호를 입력 받습니다.
해당 함수는 해당 계정의 '.fd.state' 영역에 저장된 값이 '0'이 아닐 경우 비밀번호를 변경할 수 있습니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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 ) } else { puts("WeEnter haveyour notNew any candyPassword."); } return __readfsqword(0x28u) ^ v3; } |
setBoard()
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 malloc() 함수를 이용해 1200 byte의 Heap 영역을 할당 받습니다.
- 해당 함수는 해당 영역에 사용자로 부터 값을 입력 받아 저장합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
unsigned __int64 setBoard() { unsigned __int64 v0; // ST08_8 v0 = UserInput(gAccount[num].fd->pw, 8LL); } } } } return __readfsqword(0x28u) ^ v4; puts("Please enter a comment for candy."); board = (__int64)malloc(1200uLL); UserInput(board, 1200LL); return __readfsqword(0x28u) ^ v0; } |
.init_array
- 다음과 같이 .init
Code Block | ||
---|---|---|
| ||
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를 사용합니다.
...
title | Struct IDPW, ACCOUNT |
---|
} |
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;
} |
해당 함수는 다음과 같은 기능을 합니다.
전역 변수 gAccount[0]에 다음과 같은 값을 저장 합니다.
state = 1
number = 1
fd→id = "Admin"
fd→pw = "admin"
state 변수에 1을 저장하고 있으며, 해당 계정으로 "orderMenu", "Account" 기능을 이용 할 수 있습니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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;
} |
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) ^ v2v4; } |
addToOrderList
해당 함수는 다음과 같은 기능을 합니다.
해당 함수는 choiceCandy() 함수를 사용해 주문할 사탕의 번호를 입력받습니다.
해당 함수는 malloc() 함수를 사용해 24 byte의 heap 영역을 할당받습니다.
해당 함수는 해당 영역에 주문할 사탕의 정보를 저장합니다.
...
Structure of Exploit code
Panel |
---|
|
- The following information is required for an attack:
Panel |
---|
|
Information for attack
Panel |
---|
...