--------------------------------------------------- - Shall we play a game? - ---------------------------------------------------
You have given some gold coins in your hand however, there is one counterfeit coin among them counterfeit coin looks exactly same as real coin however, its weight is different from real one real coin weighs 10, counterfeit coin weighes 9 help me to find the counterfeit coin with a scale if you find 100 counterfeit coins, you will get reward :) FYI, you have 60 seconds.
- How to play - 1. you get a number of coins (N) and number of chances (C) 2. then you specify a set of index numbers of coins to be weighed 3. you get the weight information 4. 2~3 repeats C time, then you give the answer
- Example - [Server] N=4 C=2 # find counterfeit among 4 coins with 2 trial [Client] 0 1 # weigh first and second coin [Server] 20 # scale result : 20 [Client] 3 # weigh fourth coin [Server] 10 # scale result : 10 [Client] 2 # counterfeit coin is third! [Server] Correct!
- Ready? starting in 3 sec... -
簡單來說就是一個要在指定輪數 C 內找到藏在 N 個硬幣(一般重量:10)中的假幣(重量:9),而我們可以有 C 次機會向 server 詢問某些 index 的硬幣重量加起來是多少,總共有 100 回合的遊戲,並必須在 60 秒內完成。
for i inrange(100): res = r.recvline().strip().decode() N = int(res.split(' ')[0].split('=')[1]) C = int(res.split(' ')[1].split('=')[1]) left, right = 0, N-1 while left < right: mid = (left + right) // 2 payload = b" ".join([str(j).encode() for j inrange(left, mid+1)]) r.sendline(payload)
ans = int(r.recvline().strip()) if ans % 10 == 0: # counterfeit on the right left = mid + 1 else: right = mid C -= 1 for _ inrange(C): # non sense r.sendline(b'0') r.recvline() r.sendline(str(left).encode()) print(r.recvline())
r.interactive()
因為用 ssh 轉送 port 到本機速度太慢了,沒辦法在 60 秒內完成,因此直接在機器上面執行:
1 2 3 4 5 6 7 8 9 10 11 12
coin1@ubuntu:~$ cd /tmp coin1@ubuntu:/tmp$ mkdir m3t30r coin1@ubuntu:/tmp$ cd m3t30r coin1@ubuntu:/tmp/m3t30r$ vim exploit.py coin1@ubuntu:/tmp/m3t30r$ python3 exploit.py [+] Opening connection to localhost on port 9007: Done b'Correct! (0)\n' ... b'Correct! (99)\n' [*] Switching to interactive mode Congrats! get your flag [REDACTED]
blackjack
連到機器之後,來看一下檔案內容:
1 2 3 4
blackjack@ubuntu:~$ ls blackjack blackjack.c readme blackjack@ubuntu:~$ cat readme once you connect to port 9009, the "blackjack" binary will be executed under asm_pwn privilege. get flag.
RULES of VLAD's BLACKJACK --------------------------- I. Thou shalt not question the odds of this game. S This program generates cards at random. D If you keep losing, you are very unlucky!
II. Each card has a value. S Number cards 1 to 10 hold a value of their number. D J, Q, and K cards hold a value of 10. C Ace cards hold a value of 11 The goal of this game is to reach a card value total of 21.
III. After the dealing of the first two cards, YOU must decide whether to HIT or STAY. S Staying will keep you safe, hitting will add a card. Because you are competing against the dealer, you must beat his hand. BUT BEWARE!. D If your total goes over 21, you will LOSE!. But the world is not over, because you can always play again.
SHC YOUR RESULTS ARE RECORDED AND FOUND IN SAME FOLDER AS PROGRAM CHS
看起來就是個一般的 21 點遊戲。先來看一下 blackjack.c,但因為內容過多,我們先來看印出 flag 的條件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
voidcash_test()//Test for if user has cash remaining in purse { if (cash <= 0) //Once user has zero remaining cash, game ends and prompts user to play again { printf("You Are Bankrupt. Game Over"); cash = 500; askover(); } if (cash > 1000000){ FILE* fp=fopen("flag", "r"); char buf[100]; memset(buf, 0, 100); fread(buf, 1, 100, fp); printf("%s\n", buf); fclose(fp); } } // End Function
簡單明瞭,就是 cash > 1000000,因此我們接下來的目標就是觀察如何影響 cash 的值。首先先觀察一下有呼叫到 cash_test 的 play 函數內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
voidplay()//Plays game {
int p=0; // holds value of player_total int i=1; // counter for asking user to hold or stay (aka game turns) char choice3;
cash = cash; cash_test(); printf("\nCash: $%d\n",cash); //Prints amount of cash user has randcard(); //Generates random card player_total = p + l; //Computes player total p = player_total; printf("\nYour Total is %d\n", p); //Prints player total dealer(); //Computes and prints dealer total betting(); //Prompts user to enter bet amount ...
intbetting()//Asks user amount to bet { printf("\n\nEnter Bet: $"); scanf("%d", &bet);
if (bet > cash) //If player tries to bet more money than player has { printf("\nYou cannot bet more money than you have."); printf("\nEnter Bet: "); scanf("%d", &bet); return bet; } elsereturn bet; } // End Function
可以看到,他第一次如果賭了超過原有的 cash 值,會要求重新輸入,但第二次則完全沒有檢查就直接 return bet。這表示我們可以先輸入一個超大的值,觸發 if 條件式之後,就可以繞過這個限制。
... while(i<=21) //While loop used to keep asking user to hit or stay at most twenty-one times // because there is a chance user can generate twenty-one consecutive 1's { if(p==21) //If user total is 21, win { printf("\nUnbelievable! You Win!\n"); won = won+1; cash = cash+bet; printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss); dealer_total=0; askover(); }
if(p>21) //If player total is over 21, loss { printf("\nWoah Buddy, You Went WAY over.\n"); loss = loss+1; cash = cash - bet; printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss); dealer_total=0; askover(); } if(p<=21) //If player total is less than 21, ask to hit or stay { printf("\n\nWould You Like to Hit or Stay?");
scanf("%c", &choice3); while((choice3!='H') && (choice3!='h') && (choice3!='S') && (choice3!='s')) // If invalid choice entered { printf("\n"); printf("Please Enter H to Hit or S to Stay.\n"); scanf("%c",&choice3); }
if((choice3=='H') || (choice3=='h')) // If Hit, continues { randcard(); player_total = p + l; p = player_total; printf("\nYour Total is %d\n", p); dealer(); if(dealer_total==21) //Is dealer total is 21, loss { printf("\nDealer Has the Better Hand. You Lose.\n"); loss = loss+1; cash = cash - bet; printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss); dealer_total=0; askover(); }
if(dealer_total>21) //If dealer total is over 21, win { printf("\nDealer Has Went Over!. You Win!\n"); won = won+1; cash = cash+bet; printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss); dealer_total=0; askover(); } } ...
可以發現無論什麼情況,在輸掉(蠻簡單的,只要在小點數時 Stay 就高機率會輸,因為莊家很容易比小點數更大)時程式都會執行 cash = cash - bet,而 bet 就是我們剛剛的回傳值,因此可以在第二次輸入時,輸入一個很小的負數,這樣最後在計算 cash 時就會變成很大的數字,達到印出 flag 的要求。
最後來看一下 askover:
1 2 3 4 5 6 7 8 9 10
voidaskover()// Function for asking player if they want to play again { ... if((choice1 == 'Y') || (choice1 == 'y')) // If yes, continue. { printf("\033[2J\033[1;1H"); play(); } ... } // End function
輸掉之後會詢問是否還要 play again,若選擇 Y 會再次執行 play 函數,就會執行到 cash_test,此時我們的 cash 數值已經足夠,即可獲得 flag。
voidhelp(){ printf("- nLotto Rule -\n"); printf("nlotto is consisted with 6 random natural numbers less than 46\n"); printf("your goal is to match lotto numbers as many as you can\n"); printf("if you win lottery for *1st place*, you will get reward\n"); printf("for more details, follow the link below\n"); printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n"); printf("mathematical chance to win this game is known to be 1/8145060.\n"); }
intmain(int argc, char* argv[]){
// menu unsignedint menu;
while(1){
printf("- Select Menu -\n"); printf("1. Play Lotto\n"); printf("2. Help\n"); printf("3. Exit\n");