...
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수는 전역 변수 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 | ||||
---|---|---|---|---|
| ||||
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번째 구조체는 사용자가 생성한 계정의 정보가 저장됩니다.
- 첫번째 구조체에는 'Admin' 계정 정보가 저장되어 있습니다.
...
- House of lore 취약성은 다음과 같은 Fake chunk가 필요합니다.
delAccount() 함수를 이용해 gAccount[1].fd 영역에 Free chunk의 Head 주소를 저장 할 수 있습니다.
charge() 함수를 이용해 gAccount[1].bk, gAccount[2].bk 영역의 값을 변경 할 수 있습니다.
...
title | Fake chunk |
---|
...
함수는 전역 변수 "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 )
{
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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번째 구조체는 사용자가 생성한 계정의 정보가 저장됩니다.
- 첫번째 구조체에는 'Admin' 계정 정보가 저장되어 있습니다.
- 해당 구조체는 전역 변수로 선언되어 있습니다.
Panel | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
- House of lore 취약성은 다음과 같은 Fake chunk가 필요합니다.
delAccount() 함수를 이용해 gAccount[1].fd 영역에 Free chunk의 Head 주소를 저장 할 수 있습니다.
charge() 함수를 이용해 gAccount[1].bk, gAccount[2].bk 영역의 값을 변경 할 수 있습니다.
Panel | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||
|
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영역에 값을 덮어 쓸수 있습니다.
- delAccount() 함수에 의해 gAccount[1].fd 영역의 값이 변경됩니다.
하지만 패스워드를 변경하기 위해서는 "gAccount[1].fd→state"의 값이 '0'이 아니어야 합니다.
Panel | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||
|
다음과 같이 UAF 취약성을 사용해 "gAccount[1].fd→state" 값을 변경할 수 있습니다.
ACCOUNT 구조체의 크기와
- "gAccount[1].fd" 영역에 저장된
Structure of Exploit code
...