某文章付费阅读系统代码审计-XSS

漏洞版本:v1.1_build20200713
XSS漏洞复现:
局部过滤函数removexss,文件位置:www/conn/function.php
函数代码:

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
function removexss($val) {
$val = preg_replace ( '/([\x00-\x08\x0b-\x0c\x0e-\x19])/', '', $val );

$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for($i = 0; $i < strlen ( $search ); $i ++) {

$val = preg_replace ( '/(&#[xX]0{0,8}' . dechex ( ord ( $search [$i] ) ) . ';?)/i', $search [$i], $val );

$val = preg_replace ( '/(&#0{0,8}' . ord ( $search [$i] ) . ';?)/', $search [$i], $val );
}

$ra1 = array (
'javascript',
'vbscript',
'expression',
'applet',
'meta',
'xml',
'blink',
'script',
'object',
'iframe',
'frame',
'frameset',
'ilayer',
'bgsound'
);
$ra2 = array (
'onabort',
'onactivate',
'onafterprint',
'onafterupdate',
'onbeforeactivate',
'onbeforecopy',
'onbeforecut',
'onbeforedeactivate',
'onbeforeeditfocus',
'onbeforepaste',
'onbeforeprint',
'onbeforeunload',
'onbeforeupdate',
'onbegin',
'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',
'ontoggle',
'onunload'
);
$ra = array_merge ( $ra1, $ra2 );

$found = true;
while ( $found == true ) {
$val_before = $val;
for($i = 0; $i < sizeof ( $ra ); $i ++) {
$pattern = '/';
for($j = 0; $j < strlen ( $ra [$i] ); $j ++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(&#0{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra [$i] [$j];
}
$pattern .= '/i';
$replacement = substr ( $ra [$i], 0, 2 ) . ' ' . substr ( $ra [$i], 2 );
$val = preg_replace ( $pattern, $replacement, $val );
if ($val_before == $val) {

$found = false;
}
}
}
return $val;
}

该代码把经过removexss函数的字符串进行循环正则拼接替换,如果出现了黑名单$ra中的标签/属性,则把黑名单的内容进行前两个子字符与后面的字符用空格分隔
xss1
$replacement变量把黑名单的内容进行空格分隔,如果149行的正则匹配到了黑名单中到标签/属性则进行替换。
xss
代码走到147行循环到了script标签然后进行空格分隔,到149行代码到时候该正则会匹配到我们测试的该payload进行替换。
xss
匹配替换之后继续循环至黑名单的所有标签/属性后进入第二次while循环,此时$val_before变量的值重新赋值为经过替换的payload的值,经过两次的for循环之后再进入150行的if判断为true,跳出while循环,return $val的值。

整个函数理清之后就是绕过该函数进行xss。

这里使用的payload为onrepeat事件,当重复播放svg动画时会触发该事件。无需用户交互,我们只需要搭配svg标签即可。
payload:

1
<svg><animate onrepeat=alert(1) attributeName=x dur=1s repeatCount=2 />

漏洞复现:
全局搜索调用了该函数的地方然后逐一复现漏洞。
xss