If you don't use pseudorandom numbers elsewhere,
\documentclass{article}
\ExplSyntaxOn
\sys_gset_rand_seed:n { 0 }% or whatever
\NewExpandableDocumentCommand{\hash}{}
{
\int_to_Base:nn { \int_rand:nn { 36 } { 1295 } } { 36 } % two digits
__pascals_hash_three:e { \int_to_Base:nn { \int_rand:n { 46655 } } { 36 } } % three digits
}
\cs_new:Nn __pascals_hash_three:n
{
\prg_replicate:nn { 3 - \tl_count:n { #1 } } { 0 } #1
}
\cs_generate_variant:Nn __pascals_hash_three:n { e }
\ExplSyntaxOff
\begin{document}
\begin{tabular}{|c|c|}
\hline
\textbf{HASH} & \textbf{Description} \ \hline
\hash & This is the first Hash \ \hline
\hash & This is the second Hash \
\hline
\end{tabular}
\end{document}
Fixing a seed will allow exact reproduction at every run.

You compute the probability of getting duplicates.
If you need to avoid duplicates because you have too many hashes compared to the number of digits, you lose expandability.
\documentclass{article}
\ExplSyntaxOn
\sys_gset_rand_seed:n { 0 }% or whatever
\NewDocumentCommand{\hash}{}
{
\pascals_hash:
}
\tl_new:N \l__pascals_hash_try_tl
\seq_new:N \g_pascals_hash_used_seq
\cs_new_protected:Nn \pascals_hash:
{
% generate a tentative hash
\tl_set:Ne \l__pascals_hash_try_tl
{
\int_to_Base:nn { \int_rand:nn { 36 } { 1295 } } { 36 } % two digits
__pascals_hash_three:e { \int_to_Base:nn { \int_rand:n { 46655 } } { 36 } } % three digits
}
% check for duplicate
\seq_if_in:NVTF \g_pascals_hash_used_seq \l__pascals_hash_try_tl
{% it's a duplicate, retry
\pascals_hash:
}
{% not a duplicate
\seq_gput_right:NV \g_pascals_hash_used_seq \l__pascals_hash_try_tl
\tl_use:N \l__pascals_hash_try_tl
}
}
\cs_new:Nn __pascals_hash_three:n
{
\prg_replicate:nn { 3 - \tl_count:n { #1 } } { 0 } #1
}
\cs_generate_variant:Nn __pascals_hash_three:n { e }
\ExplSyntaxOff
\begin{document}
\begin{tabular}{|c|c|}
\hline
\textbf{HASH} & \textbf{Description} \ \hline
\hash & This is the first Hash \ \hline
\hash & This is the second Hash \
\hline
\end{tabular}
\end{document}